Skip to main content
One task, end to end: watch what is happening to a company — in the news, in federal contracting, and in the physical world. It takes four calls, and the shape of it generalises to almost everything else you will build here. The whole design rests on one idea: resolve a name to an id once, then never resolve it again.
1

Resolve the name

Never filter by a company name string. Names are ambiguous, they change, and every surface spells them differently. Ask the resolver instead:
Illustrative candidate response; counts and source availability depend on the request and account:
Three fields decide what you do next:
  • entity_id is the join key. That one string is what every call below takes.
  • sources describes measured source availability. It does not establish the result count of another endpoint or prove that an entity owns no facilities. Withheld source keys are omitted; missing or failed coverage remains unknown. Inspect the actual selected-ID response.
  • identifiers is how you reach the surfaces that are keyed on someone else’s id — the SEC filings endpoints take a CIK, and the resolver already handed you one.
An empty result for a valid-looking name usually means the entity genuinely is not in the corpus, not that the query is wrong. The resolver returns no match rather than a guess. When several candidates come back, show them — do not auto-pick the top one.
2

What is happening to it

Three coded events, ranked by how much they matter rather than by when they landed:sort=significance is the default and the one you want for monitoring. Sorting by recency gives you a feed dominated by whatever a wire service published in the last hour.Start wide. The most common mistake on this API is stacking four filters on the first call and concluding the data is missing when the empty result is really your AND.
3

Read the evidence

An event is a claim; the articles are why we made it. Every event links to the story it was coded from — take the top row of that table:
Then follow the story id to the source URLs:
Twenty-two articles back that one story, and the first three are Turkish, Indian and Afghan outlets — independent coverage rather than one wire story republished, which is how you tell a well-supported event from a thin one. Build citation into your feed from the start; an alert nobody can verify gets ignored the second time it fires.
4

Fan out with the same id

This is the payoff. The entity_id from step 1 works unchanged on the other surfaces, and the sources map from step 1 already told you which of them will have rows.Press tone over time — scored by us across news coverage, per entity per story per day.
Rows come back under rows, one per bucket date, and 22 of the 27 buckets in that real response carried avg_news_tone_score: null — days with coverage but no news-tone reading. That is not a zero. Charting it as one invents a trend.Share of voice — the same tone in context: a drop reads differently when coverage volume tripled the same week.
This endpoint refuses to compute without a denominator — a share with no stated denominator is not a number. Omit one and you get 400 DENOMINATOR_REQUIRED, whose details.allowed_filters names every filter that defines one. The denominator comes back with a hash, so the same share is reproducible months later.Federal awards — the same id against USAspending.
Twenty-two SAM.gov recipient records roll up under one entity id. That collapse is the product: filtering by the name string would have matched some of the twenty-two and silently dropped the rest.Physical assets — same call shape. Step 1 reported "gem": false for Lockheed Martin, and /facilities is duly empty for it: zero rows, 200, no error. So this one runs against a resolved id that does carry assets — Chevron Corp, e_7efd743ec55a5285.
If you had filtered by name instead of by id, each of these four would have matched a different subset and you would have silently dropped half the data — without any endpoint returning an error.
Every request and response above was run on 2026-08-26 and is reproduced as returned, trimmed to the fields each step uses. Live rows carry more, and the values move with the news.

Doing the same thing through MCP

Same four steps, no HTTP. Point an agent at the MCP server and it discovers the tools itself:
Use REST when you are writing a service; use MCP when a model is deciding what to call next. The data and the gating are identical — the MCP routes to the same serve functions.

Four things worth knowing before you build

Not to all of history. Other families carry their own default, declared on each days parameter in the API reference. Pass days or an explicit date_start/date_end window whenever the window matters to your answer. Windows are bounded — an unbounded scan across the whole corpus is refused rather than served slowly.
It echoes what the server actually applied, using canonical names whatever spelling you sent. If a strict endpoint does not recognize a parameter, the request fails with 400 UNKNOWN_PARAM and names the parameters it does accept. On an endpoint still using the compatibility contract, the request may succeed but list that parameter under applied_filters.ignored — a 200 that quietly answered a different question. In either case, correct the request before using the returned data.
A null metric means the observable was not found. A 0 means it was found and measured as zero. The tone response above is the live case: most buckets have no news-tone reading, and charting them as 0 invents a trend that is not there.
List endpoints return a cursor. Offset pagination drifts as new rows land mid-walk; the cursor does not.

Next

Run it on a schedule

Hand this same question to a hosted Monitor and have it deliver new matches to you.

Recipes

Short copy-paste queries for the common shapes.