Query contents
Search the content collected for your key accounts, a page at a time.
Returns the content items of your workspace, most recent first, filtered by the JSON body you send. The list is paged with a cursor: send meta.cursor.next back as cursor until meta.cursor.has_more is false. It returns 25 items per page by default: set limit (up to 100) to get more or fewer. The filters travel in a JSON body, so lists of domains, handles or URLs are not limited by the length of a URL.
Route
Access modes
Use a workspace API key. The workspace is inferred from the key.
Authorization: Bearer sk_live_...Body parameters
Every field is optional: an empty body returns the first page of every content item. A field that is not listed is refused with 422.
| 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: reused with other filters, or sent empty, it is refused with 422. See Pagination. |
| limit | integer | Number of items per page, from 1 to 100. Defaults to 25. |
| created_at_from | string | Only contents created at or after this moment. ISO 8601 date and time with a UTC offset, for example 2026-09-01T00:00:00Z. A date without a time is refused with 422. Both bounds are included. |
| created_at_to | string | Only contents created at or before this moment. ISO 8601 date and time with a UTC offset, for example 2026-09-01T00:00:00Z. A date without a time is refused with 422. Both bounds are included. |
| content_types | string[] | Only contents of these types: linkedin_comment, recently_promoted, linkedin_reaction, linkedin_post, linkedin_company_post, linkedin_job_posting, web_article, press_release, blog_post. |
| company_ids | integer[] | Only results whose company is one of these companies. Pass company IDs, as returned by Query companies. Between 1 and 100 values. |
| company_domains | string[] | Only results whose company has one of these website domains, for example acme.com. Between 1 and 100 values. |
| company_linkedin_handles | string[] | Only results whose company has one of these LinkedIn handles, the last part of the LinkedIn page address. Between 1 and 100 values. |
| company_linkedin_urls | string[] | Only results whose company has one of these LinkedIn page URLs. Between 1 and 100 values. |
| lead_id | integer | Only content written by this lead. Use the lead.id shown on each content item, or an ID from the leads endpoints. |
| response_format | string | normalized (default) or concise. concise leaves details out. See Response formats. |
The four company filters add up: a result matches when its company is any of the company_ids, or has any of the domains, LinkedIn handles or LinkedIn URLs you send. A value that matches no company in your workspace matches nothing, so the answer is an empty list, never an error. The company filters and every other filter all have to match.
Items are sorted from the newest to the oldest. Items created at the same moment keep a stable order, so no item is skipped or repeated from one page to the next.
Example request
curl -X POST "https://api.getsillage.com/api/v3/contents/query" \
-H "Authorization: Bearer $SILLAGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"limit":10,"content_types":["linkedin_post"],"company_domains":["acme.com","example.com"]}'Success response
Status: 200 OK
{
"data": [
{
"id": 120384,
"content_type": "linkedin_post",
"created_at": "2026-06-12T09:15:00.000Z",
"details": {
"linkedin_post_id": "7440055204630249472",
"is_repost": false,
"linkedin_url": "https://www.linkedin.com/posts/sillage-signals_activity-7440055204630249472-vKkM",
"text": "Thank you for featuring us!",
"posted_at": "2026-06-12T09:00:00.000Z",
"engagement": {
"likes": 12,
"comments": 2,
"shares": 1
},
"author": {
"type": "person",
"full_name": "Jean Dupont",
"first_name": "Jean",
"last_name": "Dupont",
"linkedin_url": "https://www.linkedin.com/in/jeandupont",
"linkedin_headline": "VP Sales @ Acme",
"position": "VP Sales",
"avatar_url": "https://example.com/jeandupont.png"
}
},
"lead": {
"id": 3301,
"full_name": "Jean Dupont",
"first_name": "Jean",
"last_name": "Dupont",
"linkedin_url": "https://www.linkedin.com/in/jeandupont",
"linkedin_headline": "VP Sales @ Acme",
"position": "VP Sales",
"avatar_url": "https://example.com/jeandupont.png"
},
"company": {
"id": 8421,
"name": "Acme",
"domain": "acme.com",
"linkedin_url": "https://www.linkedin.com/company/acme",
"linkedin_handle": "acme",
"logo_url": "https://example.com/acme-logo.png"
}
}
],
"meta": {
"cursor": {
"next": "eyJjcmVhdGVkQXQiOiIyMDI2LTA2LTEyVDA5OjE1OjAwLjAwMFoiLCJpZCI6MTIwMzg0fQ.k3Jx0w",
"has_more": true
},
"resolved_companies": [
{
"company": {
"id": 8421,
"name": "Acme",
"domain": "acme.com",
"linkedin_url": "https://www.linkedin.com/company/acme",
"linkedin_handle": "acme",
"logo_url": "https://example.com/acme-logo.png"
},
"matched_by": "company_domains",
"identifier": "acme.com"
},
{
"company": {
"id": 9103,
"name": "Example",
"domain": "example.com",
"linkedin_url": "https://www.linkedin.com/company/example",
"linkedin_handle": "example",
"logo_url": null
},
"matched_by": "company_domains",
"identifier": "example.com"
}
]
}
}When nothing matches, data is an empty array and meta.cursor.has_more is false.
Response fields
| Field | Type | Description |
|---|---|---|
| data[].id | integer | ID of the content item. |
| data[].content_type | string | The kind of content, one of the values listed in Content types. |
| data[].created_at | string | When the content item was created, in ISO 8601. |
| data[].details | object | What the content is about. Its shape depends on content_type. Left out with response_format set to concise. |
| data[].lead | object or null | The lead who posted, commented or reacted, when they are one of your leads, otherwise null. 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. |
| data[].company | object or null | The company the content is associated with, when it is one of the workspace's companies, otherwise null. A summary: id (its ID in your workspace), name, domain, linkedin_url, linkedin_handle and logo_url. |
| meta.cursor.next | string or null | Pass it as cursor to fetch the next page. null on the last page. |
| meta.cursor.has_more | boolean | true when more contents exist after this page, false on the last page. |
| meta.resolved_companies | array | The companies your company filters matched. See meta.resolved_companies. |
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. |
Response formats
response_format sets what the details field of each item contains:
normalized(default): a clean object that depends on thecontent_type. See the shapes below.concise:detailsis left out. This is the lightest answer, useful when you only need IDs, types and dates.
Any other value is refused with 422.
Every field inside details is left out when we could not extract it, rather than sent as null, so treat each field as optional, except the keys of an author, which are always present. engagement has the same flat shape for posts and comments: likes, comments (comments on a post, or replies to a comment), shares (posts only), and a count per reaction type (praise, empathy, interest, appreciation) when the source gives it.
LinkedIn posts (linkedin_post, linkedin_company_post)
{
"linkedin_post_id": "7440055204630249472",
"linkedin_original_post_id": "7439950617915080704",
"is_repost": true,
"linkedin_url": "https://www.linkedin.com/posts/sillage-signals_activity-7440055204630249472-vKkM",
"text": "Thank you for featuring us!",
"posted_at": "2026-03-18T15:23:10.325Z",
"engagement": {
"likes": 1,
"comments": 0,
"shares": 0,
"praise": 0,
"empathy": 0
},
"author": {
"type": "company",
"id": 4012,
"name": "Sillage",
"domain": "getsillage.com",
"linkedin_url": "https://www.linkedin.com/company/sillage-signals/",
"linkedin_handle": "sillage-signals",
"logo_url": "https://example.com/sillage-logo.png"
},
"mentions": {
"profiles": [
{
"full_name": "Arnaud Weiss",
"linkedin_url": "https://www.linkedin.com/in/arnaud-weiss-60b6758a"
}
],
"companies": [
{
"name": "Salesforce",
"linkedin_url": "https://www.linkedin.com/company/salesforce/"
}
]
},
"image_urls": [
"https://media.licdn.com/dms/image/v2/D4E22AQE.../feedshare-shrink_800/0/1710000000000"
]
}linkedin_post_id is the post's own ID on LinkedIn and linkedin_url links to it. image_urls lists the URLs of the images attached to the post. For a repost, is_repost is true and linkedin_original_post_id is the ID of the original post on LinkedIn. author.type is person or company: a person author carries full_name, first_name, last_name, linkedin_url, linkedin_headline, position and avatar_url; a company author carries id (its ID in the workspace, null when it is not one of the workspace's companies), name, domain, linkedin_url, linkedin_handle and logo_url. The keys of an author are always present, null when unknown. Each entry of mentions.profiles has just a full_name and a linkedin_url, and each entry of mentions.companies just a name and a linkedin_url.
LinkedIn comments (linkedin_comment)
{
"linkedin_comment_id": "7175492449631371265",
"text": "Congrats on the launch!",
"linkedin_url": "https://www.linkedin.com/feed/update/urn:li:activity:123?commentUrn=456",
"posted_at": "2026-03-18T15:05:14.196Z",
"engagement": {
"comments": 0,
"likes": 1,
"praise": 0,
"empathy": 0,
"interest": 0,
"appreciation": 0
},
"author": {
"type": "person",
"full_name": "Jean Dupont",
"first_name": "Jean",
"last_name": "Dupont",
"linkedin_url": "https://www.linkedin.com/in/jeandupont",
"linkedin_headline": "VP Sales @ Acme",
"position": "VP Sales",
"avatar_url": "https://example.com/jeandupont.png"
},
"post": {
"content_id": 10762,
"linkedin_url": "https://www.linkedin.com/feed/update/urn:li:activity:123",
"text": "We just closed our pre-seed.",
"author": null
}
}linkedin_comment_id is the comment's own ID on LinkedIn and linkedin_url links to it. engagement.comments is the number of replies to the comment. post is the post the comment was left on: content_id is its ID among your contents, to pass to Get a content item to read that post (null when that post is not among your contents), linkedin_url and text are the post's link and text, and author is who published it, typed like the author of a post (null when unknown). A key of post the source does not hold is null.
LinkedIn reactions (linkedin_reaction)
{
"reaction_type": "praise",
"author": {
"type": "person",
"full_name": "Jean Dupont",
"first_name": "Jean",
"last_name": "Dupont",
"linkedin_url": "https://www.linkedin.com/in/jeandupont",
"linkedin_headline": "VP Sales @ Acme",
"position": "VP Sales",
"avatar_url": "https://example.com/jeandupont.png"
},
"post": {
"content_id": 120384,
"linkedin_url": "https://www.linkedin.com/posts/sillage-signals_activity-7440055204630249472",
"text": "We just closed our pre-seed.",
"author": {
"type": "company",
"id": 4012,
"name": "Sillage",
"domain": "getsillage.com",
"linkedin_url": "https://www.linkedin.com/company/sillage-signals/",
"linkedin_handle": "sillage-signals",
"logo_url": "https://example.com/sillage-logo.png"
}
}
}reaction_type is like, praise, interest, empathy, entertainment or appreciation. New values may be added; ignore values you don't know. author is the person who reacted and post is the post they reacted to, described like the post of a comment.
LinkedIn job postings (linkedin_job_posting)
{
"linkedin_job_id": "9988776655",
"linkedin_url": "https://www.linkedin.com/jobs/view/senior-software-engineer-at-sillage-9988776655",
"title": "Senior Software Engineer",
"min_salary": null,
"max_salary": null,
"location": "Paris, Île-de-France, France",
"posted_at": "2025-11-12T00:00:00.000Z",
"description": "About Us. Sillage helps go-to-market teams turn public signals into pipeline."
}linkedin_job_id is the job posting's own ID on LinkedIn, location is the job's location as written on the job offer (free text) and description is its full text. min_salary and max_salary are null when no salary range is given for the posting; otherwise each is a number or a text such as "$100K/yr".
Other content types
For recently_promoted, web_article, press_release and blog_post, details is { "text": "..." } when text is available, and left out otherwise.
Errors
| Status | Meaning |
|---|---|
| 401 | The API key is missing or invalid. |
| 422 | The body is not valid: an unknown field, a value that is not allowed, a date without a time, a limit outside 1 to 100, or a cursor that was not issued to you by this endpoint, was issued for other filters, or is empty. A range that ends before it starts (created_at_from after created_at_to) is refused with the code invalid_date_range; errors.created_at_from says "must be on or before created_at_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. For example, with limit set to 500:
{
"type": "about:blank",
"title": "Unprocessable Entity",
"status": 422,
"code": "invalid_body",
"detail": "Invalid request body",
"instance": "/api/v3/contents/query",
"errors": {
"limit": ["must be at most 100"]
}
}