Skip to main content
Most signal endpoints answer “what happened at this company”, which means you have to know the company first. ICP signals invert that: you describe the market you sell into once, and TAMTAM tells you which companies in it just did something worth a call — including companies you have never heard of and have not added to your Key Accounts.

How it fits together

1

Define an ICP

An ICP is your market, expressed as criteria: HQ countries, headcount band, industries, plus optional research questions such as “does this company use Microsoft 365?”. Create one with Create an ICP criteria.
2

Watch it

Create or update an ICP signal watch tells us which signal types to detect for that ICP, and optionally which news topics to restrict to. Safe to call repeatedly — it creates the watch the first time and reconfigures it afterwards.
3

Poll the feed

List ICP signals returns what has been detected. Save next_cursor and pass it back to pick up where you left off.

Signal types

News topics

news covers a fixed, curated set of event types. Each event carries its topic in payload.topic, so you can route on it without parsing the headline. The same values are what you pass in news_topics when creating a watch: By default a watch searches all of them. Send news_topics to narrow it:
Omit news_topics, or send [], to go back to every topic. An unrecognised topic is rejected with a 422 listing the supported set, rather than quietly ignored — a typo in a topic name and a market with no news look identical once a run is over.
Narrowing is worth doing even if you would happily read every topic. A run spends a fixed article budget across (markets × topics), so a watch on one topic reads that topic about five times as deeply as a watch on all five — same cost, more of the thing you actually want.
The list is curated rather than free-text on purpose. Each topic pairs a per-language search query with instructions telling the extractor which company an article of that kind is about — for a funding round the subject is the company raising, never the investor; for a launch it is the company behind the product, not the product. A free-text topic would search fine and then misattribute or miss the company, which is the failure that is hard to notice from the outside.
Searches run in the language of each of your ICP’s markets, currently English, French, Dutch and German. A market whose language is not covered is still searched, in English, which finds the subset its local press published in English. Tell us which markets matter to you and we will add them.
If the event you care about is not in that table, it will not appear — no topic is inferred from your ICP. Ask and we will look at adding it: a topic is a query plus an extraction rule per language, not a new pipeline. job_posting needs your ICP to carry a job-offers criterion — the titles, description keywords or technologies that make a role interesting to you. Without one there is nothing to look for: nearly every company is hiring, so we would return your entire ICP every day. If you subscribe to job_posting and see no events, check that criterion first.

Polling

The feed is a stream you walk forward, not a list you re-read.
Prefer not to poll? Webhooks push these same events to your own URL within minutes. Build the poller anyway: the cursor is how you catch up on anything a push missed, and it stays the source of truth.
Store next_cursor and pass it back on the next call. It comes back on every non-empty page, including the last, because the feed has no end — only a current position. has_more: false means you are caught up, not finished.
Delivery is at-least-once. Under retries or overlapping detection runs you can be handed an event you have already seen. Every event has a stable id; record the ones you have processed and skip repeats. Do not treat arrival as proof of first arrival.
Hourly is a sensible cadence. Polling every minute will mostly return empty pages: news takes hours to days to surface, and detection runs on a daily sweep.

Two things about timing

occurred_at is when the event happened in the world; detected_at is when we found it. They can differ by days, and the feed is ordered by detected_at. So as you page forward, occurred_at can move backwards between consecutive events — an article published last Tuesday can be detected after one published yesterday. Order on detected_at if you need a stable sequence; use occurred_at to tell the seller how fresh the news is. News occurred_at carries day precision only: it is the publication date, so it arrives as midnight UTC.

What an event means

An event means the company cleared your whole ICP at the time we detected it, research questions included. icp_fit records which criteria matched, snapshotted then — so it still explains the alert months later, after you have edited the ICP. Two consequences of checking properly rather than guessing:
  • A company we have never researched is held back until its research completes, so its first signal can arrive a cycle after the article did. We would rather be a day late than tell you a company matches when it does not.
  • An ICP whose criteria we cannot evaluate for a given company will not produce events for it. If a watch is quieter than you expect, icp_fit on the events you did get shows which criteria are doing the filtering.

Working with an event

company_linkedin_id is the join key to the rest of the API: source_ref_id, when present, is the id of the stored article behind a news event.

Credits

Creating and reading watches is free, and so is polling the feed. Detection itself consumes credits when it has to research a company against your ICP’s research questions — the same cost as evaluating that company’s ICP fit anywhere else, charged once per company rather than once per signal. See Credits.

Shadow mode

A watch created with is_enabled: false keeps detecting and recording events without them being delivered onward. Useful for seeing what an ICP would produce, and at what volume, before wiring it into a workflow that emails your sales team.