Query signal detections
Page through the signal detections of your workspace.
Returns a page of the signal detections of your workspace, most recent detection first. It returns 25 detections per page by default: set limit (up to 100) to get more or fewer. Every filter is optional: an empty body returns the first page of every detection from the last 90 days. A search returns the detections rated top priority, the ones shown in the app. A detection read by ID may not appear in a search.
Route
Access modes
Use a workspace API key. The workspace is inferred from the key.
Authorization: Bearer sk_live_...Cursor pagination
This search pages by cursor. See also Pagination.
- Send
limit(1 to 100, default 25) and, from the second page on,cursor. - Each response carries
meta.cursor.next, the cursor of the next page, andmeta.cursor.has_more. Whenhas_moreisfalseyou are on the last page andnextisnull. - A cursor is opaque: pass it back as you received it. It only works on this endpoint, for the workspace and the filters it was issued for.
- A cursor that is altered, empty, issued for other filters, or not issued by this endpoint for this workspace, is rejected with a
422.limitmay change from one page to the next.
To get a total, call Count signal detections with the same filters.
Body parameters
All fields are optional.
| Field | Type | Description |
|---|---|---|
| cursor | string | The meta.cursor.next of the previous response, to fetch the next page. Omit it for the first page. It only works with the filters it was issued for. |
| limit | integer | Number of detections per page, from 1 to 100. Defaults to 25. |
| signal_date_from | string (date-time) | Only detections whose signal date is at or after this moment, the moment itself included. Defaults to 90 days before now. A value more than 180 days before now is refused with 422. See Dates. |
| signal_date_to | string (date-time) | Only detections whose signal date is at or before this moment, the moment itself included. |
| detected_at_from | string (date-time) | Only detections first seen at or after this moment, the moment itself included. |
| detected_at_to | string (date-time) | Only detections first seen at or before this moment, the moment itself included. |
| signal_setup_id | integer | Only detections produced by this signal setup. |
| signal_run_id | integer | Only detections from this run. Pass the id of a signal run. |
| signal_types | string[] | Only detections of these signal types: at least one value from the Signal types list. An unknown value is rejected with a 422. |
| company_ids | integer[] | Only detections whose company is one of these companies. Pass company IDs, as returned by POST /v3/companies/query. 1 to 100 values. See Company filters. |
| company_domains | string[] | Only detections whose company has one of these website domains, for example acme.com. 1 to 100 values. |
| company_linkedin_handles | string[] | Only detections whose company has one of these LinkedIn handles, the last part of the LinkedIn page address, for example acme-corp. 1 to 100 values. |
| company_linkedin_urls | string[] | Only detections whose company has one of these LinkedIn page URLs. 1 to 100 values. |
| lead_id | integer | Only detections linked to this lead. Matches detections directly linked to the lead, not detections attributed to it indirectly. |
| countries | string[] | Only detections whose linked lead is located in one of these countries, for example ["France", "Germany"]. Combined with cities: a detection matches when its lead is in one of the countries or one of the cities. Detections without a linked lead never match. 1 to 100 values. |
| cities | object[] | Only detections whose linked lead is located in one of these cities. Each city is paired with its country: { "name": "Paris", "country": "France" }. Detections without a linked lead never match. 1 to 100 values. |
Dates
All four date filters include the moment you give. Each one is an ISO 8601 date and time with a UTC offset, for example 2026-09-01T00:00:00Z. A date without a time, such as 2026-09-01, is rejected with a 422. signal_date_from cannot be more than 180 days before now: an older value is refused with a 422 whose message gives the earliest accepted moment.
Company filters
company_ids, company_domains, company_linkedin_handles and company_linkedin_urls add up: a detection matches when its company is any of the IDs, domains, handles or URLs you send. A value that matches no company in the workspace matches nothing, so the answer is an empty list, never an error. The company filters combine with every other filter, which all have to match.
Example request
curl -X POST "https://api.getsillage.com/api/v3/signals/query" \
-H "Authorization: Bearer $SILLAGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"limit":25,"signal_types":["keyword_detection"],"signal_date_from":"2026-09-01T00:00:00Z"}'Success response
Status: 200 OK
{
"data": [
{
"id": 101,
"signal_type": "keyword_detection",
"details": {
"post": {
"content_id": 5100,
"linkedin_url": "https://www.linkedin.com/feed/update/urn:li:activity:7100000000000000000/",
"text": "Excited to share how AI is transforming our product roadmap this quarter.",
"author": {
"type": "person",
"full_name": "Jane Doe",
"first_name": null,
"last_name": null,
"linkedin_url": "https://linkedin.com/in/janedoe",
"linkedin_headline": "VP Product at Acme",
"position": null,
"avatar_url": "https://example.com/janedoe.png"
}
},
"keywords_found": ["AI"]
},
"detected_at": "2026-06-01T10:00:00.000Z",
"signal_date": "2026-05-31T14:20:00.000Z",
"lead": {
"id": 456,
"full_name": "Jane Doe",
"first_name": "Jane",
"last_name": "Doe",
"linkedin_url": "https://linkedin.com/in/janedoe",
"linkedin_headline": "VP Product at Acme",
"position": "VP Product",
"avatar_url": "https://example.com/janedoe.png"
},
"company": {
"id": 789,
"name": "Acme",
"domain": "acme.com",
"linkedin_url": "https://www.linkedin.com/company/acme",
"linkedin_handle": "acme",
"logo_url": "https://example.com/acme-logo.png"
},
"signal_setup_id": 12,
"source_url": "https://www.linkedin.com/feed/update/urn:li:activity:7100000000000000000/",
"author": {
"full_name": "Jane Doe",
"first_name": null,
"last_name": null,
"linkedin_url": "https://linkedin.com/in/janedoe",
"linkedin_headline": "VP Product at Acme",
"position": null,
"avatar_url": "https://example.com/janedoe.png"
},
"excerpt": "Excited to share how AI is transforming our product roadmap this quarter."
}
],
"meta": {
"cursor": {
"next": "eyJkZXRlY3RlZEF0IjoiMjAyNi0wNi0wMVQxMDowMDowMC4wMDBaIiwiaWQiOjEwMX0.k3VQ2Zr1",
"has_more": true
},
"resolved_companies": []
}
}To fetch the next page, send meta.cursor.next as cursor.
Response fields
| Field | Type | Description |
|---|---|---|
| data[] | array | The signal detections of the page, most recent detection first. |
| meta.cursor.next | string | null | Cursor of the next page. null on the last page. |
| meta.cursor.has_more | boolean | Whether more detections follow this page. |
| meta.resolved_companies | array | The companies your company filters matched. See meta.resolved_companies. |
Each item of data has these fields:
| Field | Type | Description |
|---|---|---|
| id | integer | ID of the signal detection. |
| signal_type | string | null | What the detection is about, for example keyword_detection or champion_job_change. See Signal types. |
| details | object | null | The payload of the detection. Its shape depends on signal_type, see Details by signal type. |
| detected_at | string (date-time) | null | When the detection was first seen. |
| signal_date | string (date-time) | null | Date of the underlying event. |
| lead | object | null | The lead the detection is about, as a summary: id (the ID of the lead, as in Get a lead), full_name, first_name, last_name, linkedin_url, linkedin_headline, position and avatar_url. For a detection without a lead of its own, such as a keyword match, it is the lead of the author of the content when known. null when the author is unknown. |
| company | object | null | The company the detection is about, as a summary: id (the ID returned by POST /v3/companies/query), name, domain, linkedin_url, linkedin_handle and logo_url. null when the detection is not about a company. |
| signal_setup_id | integer | null | ID of the signal setup that produced the detection. null when the detection does not belong to a current signal setup, for example when it comes from an older signal or from a setup that was deleted. This is the same rule as for signal runs. |
| source_url | string | null | Link to the content behind the detection (the post, the interaction or the job posting). null when the detection has no public link. |
| author | object | null | The person behind the content, when known: full_name, first_name, last_name, linkedin_url, linkedin_headline, position and avatar_url, each nullable. null for a detection about a company only or without an author. |
| excerpt | string | null | A short text extract of the content (post text, comment or job description). null when no text is available. |
meta.resolved_companies
Lists the companies your company filters matched, one entry per company and per filter value that matched it. It is an empty array when you send no company filter.
| Field | Type | Description |
|---|---|---|
| company | object | The matched company, as a summary: id (its ID in your workspace), name, domain, linkedin_url, linkedin_handle and logo_url. |
| matched_by | string | The filter that matched this company: company_ids, company_domains, company_linkedin_handles or company_linkedin_urls. |
| identifier | string | The value you sent in that filter, a domain, a handle, a URL, or a company ID as a string. |
Details by signal type
details is the payload of the detection and its shape depends on signal_type. source_url, author and excerpt give you the most relevant link, person and text without reading details. A key the source does not hold is null. New values may be added to the lists below; ignore the ones you don't know.
A post is always described the same way, under post: content_id is the ID of the matching content in the workspace, usable with the contents endpoints (null when no matching content exists), linkedin_url is the post's LinkedIn URL, text is its text and author is who published it. post.author has a type of "person" (the keys of a person: full_name, first_name, last_name, linkedin_url, linkedin_headline, position, avatar_url) or "company" (a company page: id, name, domain, linkedin_url, linkedin_handle, logo_url). Every key is always present, null when unknown.
Keyword detections
For keyword_detection, details.post is the post the keyword matched in, and details.keywords_found lists the keywords that matched. details.post.author is null when the publisher is unknown. A keyword post has one combined author name, so first_name and last_name are null on its author.
Comment detections
Signal types ending in _inbound_comment or _outbound_comment, for example competitor_inbound_comment, carry the comment on one side (interaction) and the post it was left on the other (post). interaction has a type, a content_id (the ID of the matching content in the workspace, null when no matching content exists) and an author, who has the keys of a person. A comment's author has one combined name, so first_name and last_name are null.
…_inbound_comment: someone from the watched list commented on a post by a person at one of your accounts. …_outbound_comment: the reverse, a person at one of your accounts commented on a post by someone from the watched list. Either way, lead is the person at your account.
{
"interaction": {
"type": "comment",
"content_id": 5001,
"author": {
"full_name": "Jane Doe",
"first_name": null,
"last_name": null,
"linkedin_url": "https://linkedin.com/in/janedoe",
"linkedin_headline": "VP Product at Acme",
"position": null,
"avatar_url": "https://example.com/janedoe.png"
}
},
"post": {
"content_id": 5002,
"linkedin_url": "https://www.linkedin.com/feed/update/urn:li:activity:7100000000000000002/",
"text": "We just opened our new office in Lyon.",
"author": {
"type": "person",
"full_name": "John Smith",
"first_name": null,
"last_name": null,
"linkedin_url": "https://linkedin.com/in/johnsmith",
"linkedin_headline": "Head of Sales at Widgets",
"position": null,
"avatar_url": "https://example.com/johnsmith.png"
}
}
}Competitor activity
linkedin_comment and linkedin_reaction carry the comment or reaction (interaction) and the post it was left on (post). interaction.author is the external person who left it: the keys of a person, plus their company_name and, on a comment, their comment_text, both free text. post.author is the person whose post they engaged with, who becomes your lead. interaction.content_id is the ID of the matching content in the workspace, null when none exists, and interaction.linkedin_url is the LinkedIn URL of the comment or reaction. post.content_id is null.
{
"interaction": {
"type": "comment",
"linkedin_url": "https://linkedin.com/feed/update/urn:li:activity:7100000000000000001/",
"content_id": 5101,
"author": {
"full_name": "Jane Doe",
"first_name": "Jane",
"last_name": "Doe",
"linkedin_url": "https://linkedin.com/in/janedoe",
"linkedin_headline": "VP Product",
"position": null,
"avatar_url": null,
"company_name": "Acme",
"comment_text": "Great point, we're seeing the same shift on our side."
}
},
"post": {
"content_id": null,
"linkedin_url": "https://linkedin.com/feed/update/urn:li:activity:7100000000000000000/",
"text": "Excited to share how AI is transforming our product roadmap this quarter.",
"author": {
"type": "person",
"full_name": "John Smith",
"first_name": "John",
"last_name": "Smith",
"linkedin_url": "https://linkedin.com/in/johnsmith",
"linkedin_headline": "Head of Sales",
"position": null,
"avatar_url": null
}
}
}Content engagement
lead_liked_competitor_content and lead_commented_competitor_content use the same interaction and post shape, mirrored: post is the post published by the person you are tracking and interaction is the reaction or comment an external person left on it, the person who now surfaces as the lead. interaction has no content_id, and post.content_id is null.
lead_liked_own_content and lead_commented_own_content (a person who liked or commented on a post by your company or one of your employees) and lead_liked_influencer_content and lead_commented_influencer_content (a person who liked or commented on a post by an influencer of the list) follow the same interaction and post shape.
{
"interaction": {
"type": "comment",
"linkedin_url": "https://linkedin.com/feed/update/urn:li:activity:7100000000000000003/",
"author": {
"full_name": "Priya Shah",
"first_name": "Priya",
"last_name": "Shah",
"linkedin_url": "https://linkedin.com/in/priyashah",
"linkedin_headline": "Director of Sales",
"position": null,
"avatar_url": null,
"company_name": "Globex",
"comment_text": "This matches what we're seeing too."
}
},
"post": {
"content_id": null,
"linkedin_url": "https://linkedin.com/feed/update/urn:li:activity:7100000000000000000/",
"text": "Excited to share how AI is transforming our product roadmap this quarter.",
"author": {
"type": "person",
"full_name": "John Smith",
"first_name": "John",
"last_name": "Smith",
"linkedin_url": "https://linkedin.com/in/johnsmith",
"linkedin_headline": "Head of Sales",
"position": null,
"avatar_url": null
}
}
}Partner detections
partner_staff_comment and partner_staff_reaction (a comment or reaction left by a person on the partner's side) use the same interaction and post shape as the competitor activity above. lead_liked_partner_content and lead_commented_partner_content (a person who liked or commented on the partner's own posts) use the content engagement shape above.
Job posting keyword detections
job_posting_keyword_detection carries the keywords that matched (keywords_found) and the job posting they matched in (posting), with its title and a job_url you can open directly.
{
"keywords_found": ["Senior Engineer", "Remote"],
"posting": {
"title": "Senior Backend Engineer",
"job_url": "https://www.linkedin.com/jobs/view/1234567890"
}
}Job changes
new_job and recently_promoted describe the move of a person: previous_position is the role they leave and new_position is the role they take. Each is a job block: position, company_name, company_linkedin_url and start_date, a date without a time (YYYY-MM-DD), null when unknown.
{
"previous_position": {
"position": "Sales Manager",
"company_name": "Initech",
"company_linkedin_url": "https://linkedin.com/company/initech",
"start_date": null
},
"new_position": {
"position": "VP Sales",
"company_name": "Acme",
"company_linkedin_url": "https://linkedin.com/company/acme",
"start_date": "2026-05-01"
}
}Champion career moves
champion_job_change describes the career move of a tracked champion: change_type is the kind of move: company_change (a new company), title_change (a new role in the same company), both (a new company and a new role) or left_company (a departure, the new company is not known yet). previous_position is the role they leave and new_position is the role they move into, both job blocks as in the job changes above. It covers every champion career move, including departures where the new company is not known yet: the new_position fields are then null, as below.
{
"change_type": "left_company",
"previous_position": {
"position": "VP Product",
"company_name": "Acme",
"company_linkedin_url": "https://linkedin.com/company/acme",
"start_date": null
},
"new_position": {
"position": null,
"company_name": null,
"company_linkedin_url": null,
"start_date": null
}
}Errors
| Status | Meaning |
|---|---|
| 401 | The API key is missing or invalid. |
| 422 | The body is not valid: an unknown field, a limit outside 1 to 100, a date without a time, a signal_date_from older than 180 days, an unknown value in signal_types, a query parameter (this route takes none, and errors names the parameter), or a cursor that is empty, invalid, issued for other filters, or not issued by this endpoint. A range that ends before it starts (signal_date_from after signal_date_to or detected_at_from after detected_at_to) is refused with the code invalid_date_range; errors.signal_date_from says "must be on or before signal_date_to". |
| 429 | Rate limit exceeded. |
| 500 | An unexpected server error occurred. |
Error responses use RFC 9457 problem documents with Content-Type: application/problem+json. type is always about:blank; code tells one problem from another, detail says what went wrong this time, and errors names the field at fault.