twitter.search. The transport rules that apply to every API a seller serves - signed requests, the response envelope, timeouts, and body limits - are on the operator endpoint contract.
The price you compete with
AnyAPI publishes this API from $0.60 per 1,000 requests. That is the price a buyer sees today, so it is the price your endpoint has to come in under to win traffic.The request your endpoint receives
| Field | Type | Required | What it means |
|---|---|---|---|
cursor | string | no | Opaque pagination cursor from a previous response’s nextCursor. Omit for the first page; pass it to fetch the next page of search results. |
limit | integer | no | Page size to ask for (1-50, default 20). Some sources honor it; others always return their whole native page of about 20. A page is never trimmed after the call, so follow nextCursor to continue exactly where this response ended. Default 20. |
query | string | yes | Search query using X (Twitter) advanced-search syntax. IMPORTANT: bare terms are ANDed - a tweet must contain EVERY word, so a list of loosely related keywords matches nothing; use one short phrase or OR between alternatives (e.g. ‘anyapi OR getanyapi’). When an operator follows an OR list, put the list in parentheses, e.g. ‘(anyapi OR getanyapi) -filter:replies’: without them X applies the operator to the last alternative only, so such a query still runs as X reads it and the response’s hint carries the corrected query. You can embed X advanced-search operators directly in the query to filter results: from:username and to:username (author or recipient), since:YYYY-MM-DD and until:YYYY-MM-DD (date range), min_faves:N, min_retweets:N, min_replies:N (engagement floors), “exact phrase”, -term to exclude, filter:media and filter:links and -filter:replies (content filters), lang:en, near:city, and geocode:lat,long,radius. Examples: ‘from:OpenAI’, ‘AI agents min_faves:500 -filter:replies’, ‘nvidia since:2026-01-01 until:2026-03-01’. A query with no matches returns an empty items array; prefer the fewest words that identify the topic. |
queryType | string | no | Which posts to return: ‘Latest’ (newest first, the default), ‘Top’ (most popular), ‘Photos’ (newest posts with images), or ‘Videos’ (newest posts with video). Photos and Videos search your query as written with filter:images or filter:videos added after it, so they cost the same as a Latest search. One of: Latest, Top, Photos, Videos. Default "Latest". |
requireFields | array | no | Optional; omit it and routing is unchanged, with the cheapest source serving. Name the output fields this request must be able to return, for example nextCursor, and it is served only by a source that returns every one of them. Fields you do not name are still returned whenever the serving source has them. This can raise your price: when the cheapest source cannot return a named field, a dearer source serves, and you are quoted and charged its price. A named field can still be absent on a post that genuinely lacks it. Naming a combination that no single source returns together is refused as invalid input, with no charge. On a paginated walk it applies to the first page only; later pages stay with the source that page chose, at the price it was quoted. |
requireSinglePage | boolean | no | Set true to get up to limit results in one response instead of provider-native pages, served by a bulk provider when needed. |
Full input JSON Schema
Full input JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://getanyapi.com/schemas/twitter.search.input.json",
"example": {
"query": "openai"
},
"title": "X / Twitter search input",
"type": "object",
"additionalProperties": false,
"x-anyapi-compat-inputs": {
"lang": {
"fold_into": "query",
"template": " lang:{value}",
"wrap": true
},
"queryType": {
"fold_into": "query",
"template": " filter:{value}",
"wrap": true,
"values": {
"Photos": "images",
"Videos": "videos"
}
}
},
"required": [
"query"
],
"properties": {
"requireFields": {
"type": "array",
"uniqueItems": true,
"items": {
"type": "string",
"enum": [
"nextCursor"
]
},
"description": "Optional; omit it and routing is unchanged, with the cheapest source serving. Name the output fields this request must be able to return, for example `nextCursor`, and it is served only by a source that returns every one of them. Fields you do not name are still returned whenever the serving source has them. This can raise your price: when the cheapest source cannot return a named field, a dearer source serves, and you are quoted and charged its price. A named field can still be absent on a post that genuinely lacks it. Naming a combination that no single source returns together is refused as invalid input, with no charge. On a paginated walk it applies to the first page only; later pages stay with the source that page chose, at the price it was quoted."
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 50,
"default": 20,
"description": "Page size to ask for (1-50, default 20). Some sources honor it; others always return their whole native page of about 20. A page is never trimmed after the call, so follow nextCursor to continue exactly where this response ended."
},
"query": {
"type": "string",
"description": "Search query using X (Twitter) advanced-search syntax. IMPORTANT: bare terms are ANDed - a tweet must contain EVERY word, so a list of loosely related keywords matches nothing; use one short phrase or OR between alternatives (e.g. 'anyapi OR getanyapi'). When an operator follows an OR list, put the list in parentheses, e.g. '(anyapi OR getanyapi) -filter:replies': without them X applies the operator to the last alternative only, so such a query still runs as X reads it and the response's hint carries the corrected query. You can embed X advanced-search operators directly in the query to filter results: from:username and to:username (author or recipient), since:YYYY-MM-DD and until:YYYY-MM-DD (date range), min_faves:N, min_retweets:N, min_replies:N (engagement floors), \"exact phrase\", -term to exclude, filter:media and filter:links and -filter:replies (content filters), lang:en, near:city, and geocode:lat,long,radius. Examples: 'from:OpenAI', 'AI agents min_faves:500 -filter:replies', 'nvidia since:2026-01-01 until:2026-03-01'. A query with no matches returns an empty items array; prefer the fewest words that identify the topic."
},
"queryType": {
"type": "string",
"enum": [
"Latest",
"Top",
"Photos",
"Videos"
],
"description": "Which posts to return: 'Latest' (newest first, the default), 'Top' (most popular), 'Photos' (newest posts with images), or 'Videos' (newest posts with video). Photos and Videos search your query as written with filter:images or filter:videos added after it, so they cost the same as a Latest search.",
"default": "Latest"
},
"cursor": {
"type": "string",
"description": "Opaque pagination cursor from a previous response's nextCursor. Omit for the first page; pass it to fetch the next page of search results."
},
"requireSinglePage": {
"type": "boolean",
"description": "Set true to get up to limit results in one response instead of provider-native pages, served by a bulk provider when needed."
}
}
}
The response your endpoint must return
| Field | Type | Always present | What it means |
|---|---|---|---|
data | null or object | yes | |
data.items | array | yes | Tweet records: text, author profile, timestamp, and engagement metrics (likes, retweets, replies, views). |
data.items[].authorFollowers | integer | no | Follower count of the account that posted. |
data.items[].authorId | string | no | Numeric X user id of the account that posted, as a string. |
data.items[].authorImage | string | no | Avatar image URL of the account that posted. |
data.items[].authorName | string | no | |
data.items[].authorUsername | string | no | |
data.items[].authorVerified | boolean | no | |
data.items[].bookmarkCount | integer | no | |
data.items[].conversationId | string | no | |
data.items[].createdUtc | number | no | UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds. |
data.items[].id | string | yes | |
data.items[].inReplyToId | string or null | no | Id of the post this post directly replies to, as a string, or null when the post is not a reply. |
data.items[].isReply | boolean | no | |
data.items[].lang | string | no | |
data.items[].likeCount | integer | no | |
data.items[].media | array | no | Photo, video, and GIF attachments on the post. Empty when the post has none. |
data.items[].media[].height | integer | no | Pixel height of the media item, when the lane reports it. |
data.items[].media[].type | string | yes | One of photo, video, or gif. |
data.items[].media[].url | string | yes | Image URL. For a video or GIF this is the poster/thumbnail frame. X media URLs on pbs.twimg.com are publicly fetchable without authentication; append ?name=orig for the full-resolution original. |
data.items[].media[].videoUrl | string | no | Playable video file URL. Present only for video and gif items. |
data.items[].media[].width | integer | no | Pixel width of the media item, when the lane reports it. |
data.items[].quoteCount | integer | no | |
data.items[].replyCount | integer | no | |
data.items[].retweetCount | integer | no | |
data.items[].text | string | yes | |
data.items[].url | string | yes | |
data.items[].viewCount | integer or null | no | Number of views. Null when the source does not report it, as for posts older than X view counts. |
data.nextCursor | string or null | no | Opaque cursor for the next page of search results, or null when there are no more. Pass it back as cursor to continue. |
found | boolean | yes |
Full output JSON Schema
Full output JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://getanyapi.com/schemas/twitter.search.output.json",
"title": "X / Twitter search output",
"type": "object",
"additionalProperties": false,
"required": [
"found",
"data"
],
"properties": {
"found": {
"type": "boolean"
},
"data": {
"oneOf": [
{
"type": "null"
},
{
"type": "object",
"additionalProperties": false,
"required": [
"items"
],
"properties": {
"items": {
"x-anyapi-must-populate": true,
"type": "array",
"description": "Tweet records: text, author profile, timestamp, and engagement metrics (likes, retweets, replies, views).",
"items": {
"type": "object",
"required": [
"id",
"url",
"text"
],
"properties": {
"id": {
"x-anyapi-must-populate": true,
"type": "string"
},
"url": {
"x-anyapi-must-populate": true,
"type": "string"
},
"text": {
"x-anyapi-must-populate": true,
"type": "string"
},
"authorUsername": {
"x-anyapi-must-populate": true,
"type": "string"
},
"authorName": {
"x-anyapi-must-populate": true,
"type": "string"
},
"authorVerified": {
"type": "boolean"
},
"authorId": {
"type": "string",
"description": "Numeric X user id of the account that posted, as a string."
},
"authorImage": {
"type": "string",
"description": "Avatar image URL of the account that posted."
},
"authorFollowers": {
"type": "integer",
"description": "Follower count of the account that posted."
},
"likeCount": {
"type": "integer"
},
"retweetCount": {
"type": "integer"
},
"replyCount": {
"type": "integer"
},
"quoteCount": {
"type": "integer"
},
"viewCount": {
"type": [
"integer",
"null"
],
"description": "Number of views. Null when the source does not report it, as for posts older than X view counts."
},
"bookmarkCount": {
"type": "integer"
},
"createdUtc": {
"x-anyapi-must-populate": true,
"type": "number",
"description": "UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds."
},
"lang": {
"type": "string"
},
"isReply": {
"type": "boolean"
},
"conversationId": {
"type": "string"
},
"inReplyToId": {
"type": [
"string",
"null"
],
"description": "Id of the post this post directly replies to, as a string, or null when the post is not a reply."
},
"media": {
"type": "array",
"description": "Photo, video, and GIF attachments on the post. Empty when the post has none.",
"items": {
"type": "object",
"required": [
"type",
"url"
],
"properties": {
"type": {
"type": "string",
"description": "One of photo, video, or gif."
},
"url": {
"type": "string",
"format": "uri",
"description": "Image URL. For a video or GIF this is the poster/thumbnail frame. X media URLs on pbs.twimg.com are publicly fetchable without authentication; append ?name=orig for the full-resolution original."
},
"videoUrl": {
"type": "string",
"format": "uri",
"description": "Playable video file URL. Present only for video and gif items."
},
"width": {
"type": "integer",
"description": "Pixel width of the media item, when the lane reports it."
},
"height": {
"type": "integer",
"description": "Pixel height of the media item, when the lane reports it."
}
}
}
}
}
}
},
"nextCursor": {
"type": [
"string",
"null"
],
"description": "Opaque cursor for the next page of search results, or null when there are no more. Pass it back as cursor to continue."
}
}
}
]
}
}
}
Fields that must never come back empty
authorNameauthorUsernamecreatedUtciditemstexturl
Not found, and how it differs from empty
When the entity does not exist, answer 2xx with the not-found shape:{
"found": false,
"data": null
}
found: true and an empty items array.
This output schema declares no reason field, so found: false is the whole not-found answer.
Example request
{
"query": "openai"
}
Behavior
- Latest is the default ranking, so a request that omits queryType asks for the newest matching posts.
- There are no separate filter inputs: an author, date range, engagement floor or language is written inside the query string with X advanced-search operators such as from:, since:, until:, min_faves: and lang:en.
- A query that matches nothing returns found true with an empty items array; found false never means no matches.
- limit is a per-page maximum of 1 to 50, never a total, and nextCursor is null only when there is no further page.
- Every item carries id, url and text.
Next
- The operator endpoint contract for signing, budgets, and the live trial.
- Every API open to sellers.
- Want to sell an API that is not on that list? Email support@getanyapi.com.