Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/twittrapi-com:twittrapi-com-search-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: TwittrAPI Search API
version: 1.0.0
summary: Public Twitter/X data over REST, priced per API call.
description: 'TwittrAPI is a REST API for public Twitter/X data: users, tweets, search, followers, lists, communities, jobs and trends. Pay per call from a prepaid balance; no X developer account needed.
Authentication: send your key in the X-API-KEY header. Get a key at https://www.twittrapi.com/start (a human signs up; the key can then be given to an agent).
Responses: X''s raw JSON by default (passthrough), or add format=simple for compact, flat JSON with a fixed schema (same price).
Account actions (tag "Account actions", /client/* and /premium/*): POST endpoints that act on your own X account with its session cookie and your proxy in the JSON body (https://www.twittrapi.com/docs/x-session). Same price per call as reads; trial keys cannot call them.
Pricing (machine-readable): https://www.twittrapi.com/pricing.json. All docs in one file: https://www.twittrapi.com/llms-full.txt.
RapidAPI: the same paths on https://twitter-aio.p.rapidapi.com with the x-rapidapi-key and x-rapidapi-host headers.
Not affiliated with X Corp. Twitter and X are trademarks of X Corp. Only publicly available data is accessible.'
contact:
name: TwittrAPI support
email: support@twittrapi.com
url: https://www.twittrapi.com/docs
termsOfService: https://www.twittrapi.com/terms
license:
name: 'Proprietary: use under the terms of service'
url: https://www.twittrapi.com/terms
servers:
- url: https://api.twittrapi.com
description: Direct API
- url: https://twitter-aio.p.rapidapi.com
description: RapidAPI (x-rapidapi-key and x-rapidapi-host headers instead of X-API-KEY)
security:
- ApiKeyAuth: []
tags:
- name: Search
description: Search endpoints (read-only GET).
paths:
/search:
get:
operationId: search
summary: Search tweets
description: 'Search tweets or people with the same query syntax as the X search box.
This is the Search tab of X. The query supports X search operators, and the optional filters object adds operators for you. Results come one page at a time.
Also answers on: POST /search (same handler; the request body is ignored, parameters go in the query string); GET /search/{searchItem} (query as a URL-encoded path segment instead of ?searchItem); POST /search/{searchItem}; GET /search_v2 (same, but skips the control-character check); POST /search_v2.
Paginated: pass the bottom cursor of one page as cursor to get the next. Each page is one billed call.
An empty page from X is retried automatically, up to 9 times. You are billed once.
Mutually exclusive filter pairs (for example removeReplies and onlyReplies) return 422 when both are true.
Reference: https://www.twittrapi.com/docs/reference/search'
tags:
- Search
externalDocs:
url: https://www.twittrapi.com/docs/reference/search
parameters:
- name: searchItem
in: query
required: true
description: Search query, e.g. from:jack lang:en. Required unless filters is set. query is accepted as an alias. When the query contains operators, + is read as a space. Query plus filters may be at most 512 characters.
schema:
type: string
maxLength: 512
- name: category
in: query
required: false
description: 'Search tab: Top, Latest, People, Photos or Videos. Any other value is treated as Top.'
schema:
type: string
default: Top
enum:
- Top
- Latest
- People
- Photos
- Videos
- name: count
in: query
required: false
description: Number of results to ask X for. Sent to X as is; X treats it as a hint and decides how many it returns (follower and following pages often hold 50 to 70 users for count=20). A value that is not a number falls back to 20.
schema:
type: integer
default: 20
- name: cursor
in: query
required: false
description: Bottom cursor from the previous page. Leave it out for the first page. See Pagination.
schema:
type: string
- name: filters
in: query
required: false
description: Extra conditions as JSON, e.g. {"lang":"en","minimumLikesCount":100}. Each key becomes an operator that is appended to the query. Keys are listed below.
schema:
type: string
contentMediaType: application/json
- name: format
in: query
required: false
description: 'raw (default): X''s JSON passed through. simple: flat camelCase JSON with a fixed schema and a {data, nextCursor, previousCursor, hasNextPage} envelope. Same price. The header X-Response-Format: simple does the same.'
schema:
type: string
enum:
- raw
- simple
default: raw
responses:
'200':
description: 'Not the raw GraphQL object: {"category": "<tab>", "entries": [...]}, where entries is the instructions array of X''s SearchTimeline (data.search_by_raw_query.search_timeline.timeline.instructions). Tweets and users are inside TimelineAddEntries; the bottom cursor is an entry with cursorType "Bottom".
With format=simple: Collection of Tweet objects: {data: [Tweet], nextCursor, previousCursor, hasNextPage}. With category=People: User objects.'
headers:
X-Price-Per-Call:
description: What this call cost, in USD
schema:
type: string
X-Remaining-Balance:
description: Balance after this call (and after a refund, if any)
schema:
type: string
X-Refunded:
description: true when the call was refunded
schema:
type: string
X-Retried:
description: true when the gateway needed more than one attempt
schema:
type: string
X-Response-Format:
description: simple when the simple format was returned
schema:
type: string
content:
application/json:
schema:
anyOf:
- $ref: '#/components/schemas/TweetPage'
- $ref: '#/components/schemas/UserPage'
- $ref: '#/components/schemas/RawPassthrough'
description: 'format=simple: the simple-format schema. Default (raw): X''s JSON, passthrough.'
'400':
description: Query and filters are both empty, or filters is not valid JSON.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'403':
$ref: '#/components/responses/Forbidden'
'422':
description: The query has control characters (not checked on /search_v2), is longer than 512 characters, contains [ or ], has an unclosed double quote or unbalanced parentheses, uses an unsupported operator, or a filters value has the wrong format. X validation errors also return 422.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/ServerError'
'502':
description: 'X returned an empty timeline 9 times in a row. Body: {"error": "...", "attempts": 9}. Refunded.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
responses:
PaymentRequired:
description: No balance left, or the trial expired. Not charged. Top up in the dashboard.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Missing or unknown API key. Not charged.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
RateLimited:
description: 'Rate limit for your key exceeded. Not charged. No Retry-After header: back off exponentially.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Forbidden:
description: Key deactivated, or the endpoint is not in your plan (trial keys and account actions). Not charged.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
ServerError:
description: Something failed on our side or between us and X. Refunded (400, 407, 408, 409 or a 5xx error are refunded). Retry with backoff.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
schemas:
UnavailableTweet:
type: object
properties:
id:
type: string
unavailable:
type: boolean
const: true
required:
- id
- unavailable
description: A tweet X did not return (deleted, suspended author, withheld).
TweetPage:
type: object
description: 'Simple format: a page of tweets'
properties:
data:
type: array
items:
$ref: '#/components/schemas/Tweet'
nextCursor:
type:
- string
- 'null'
description: Pass as cursor for the next page; null on the last page
previousCursor:
type:
- string
- 'null'
description: X's top cursor, for polling newer items
hasNextPage:
type: boolean
required:
- data
- nextCursor
- previousCursor
- hasNextPage
RawPassthrough:
type: object
additionalProperties: true
description: X's own JSON (raw format, the default), passed through unchanged. The shape is X's and can change; see https://www.twittrapi.com/docs/response-format.
Error:
type: object
properties:
error:
type: string
description: Message. Do not parse it; use the status code.
errorInfo:
description: X's raw reply, on some endpoints
attempts:
type: integer
description: Search retries
errorCode:
type: string
required:
- error
User:
type: object
properties:
id:
type: string
username:
type: string
description: Handle without @
name:
type: string
description: Display name
description:
type: string
description: Bio, t.co links expanded
location:
type: string
url:
type:
- string
- 'null'
description: Website, expanded
createdAt:
type:
- string
- 'null'
format: date-time
avatarUrl:
type:
- string
- 'null'
bannerUrl:
type:
- string
- 'null'
verified:
type: boolean
description: Blue check
verifiedType:
type:
- string
- 'null'
description: e.g. Business or Government
protected:
type: boolean
description: Private account
counts:
type: object
properties:
followers:
type: integer
following:
type: integer
tweets:
type: integer
likes:
type: integer
media:
type: integer
listed:
type: integer
required:
- followers
- following
- tweets
- likes
- media
- listed
pinnedTweetIds:
type: array
items:
type: string
isAutomated:
type: boolean
required:
- id
- username
- name
- description
- location
- url
- createdAt
- avatarUrl
- bannerUrl
- verified
- verifiedType
- protected
- counts
- pinnedTweetIds
- isAutomated
Media:
type: object
properties:
type:
type: string
enum:
- photo
- video
- gif
url:
type: string
description: 'Photos: the image. Videos and GIFs: the MP4 with the highest bitrate.'
previewUrl:
type: string
description: The image (poster frame for videos)
width:
type: integer
height:
type: integer
altText:
type:
- string
- 'null'
durationMs:
type:
- integer
- 'null'
description: Videos only
required:
- type
- url
- previewUrl
- width
- height
- altText
- durationMs
Article:
type: object
properties:
id:
type: string
title:
type: string
previewText:
type:
- string
- 'null'
coverImageUrl:
type:
- string
- 'null'
publishedAt:
type:
- string
- 'null'
text:
type:
- string
- 'null'
required:
- id
- title
- previewText
- coverImageUrl
- publishedAt
- text
UserPage:
type: object
description: 'Simple format: a page of users'
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
nextCursor:
type:
- string
- 'null'
description: Pass as cursor for the next page; null on the last page
previousCursor:
type:
- string
- 'null'
description: X's top cursor, for polling newer items
hasNextPage:
type: boolean
required:
- data
- nextCursor
- previousCursor
- hasNextPage
Tweet:
type: object
properties:
id:
type: string
url:
type: string
description: https://x.com/<username>/status/<id>
text:
type: string
description: Full text, t.co links replaced by their targets
createdAt:
type:
- string
- 'null'
format: date-time
lang:
type:
- string
- 'null'
author:
anyOf:
- $ref: '#/components/schemas/User'
- type: 'null'
counts:
type: object
properties:
likes:
type: integer
retweets:
type: integer
replies:
type: integer
quotes:
type: integer
bookmarks:
type: integer
views:
type:
- integer
- 'null'
required:
- likes
- retweets
- replies
- quotes
- bookmarks
- views
media:
type: array
items:
$ref: '#/components/schemas/Media'
urls:
type: array
items:
type: string
hashtags:
type: array
items:
type: string
mentions:
type: array
items:
type: object
properties:
id:
type: string
username:
type: string
required:
- id
- username
replyTo:
anyOf:
- type: object
properties:
tweetId:
type: string
userId:
type: string
username:
type: string
required:
- tweetId
- userId
- username
- type: 'null'
quoted:
anyOf:
- $ref: '#/components/schemas/Tweet'
- $ref: '#/components/schemas/UnavailableTweet'
- type: 'null'
retweetOf:
anyOf:
- $ref: '#/components/schemas/Tweet'
- type: 'null'
conversationId:
type:
- string
- 'null'
isPinned:
type: boolean
source:
type:
- string
- 'null'
article:
anyOf:
- $ref: '#/components/schemas/Article'
- type: 'null'
required:
- id
- url
- text
- createdAt
- lang
- author
- counts
- media
- urls
- hashtags
- mentions
- replyTo
- quoted
- retweetOf
- conversationId
- isPinned
- source
- article
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-KEY
description: API key from the dashboard (https://app.twittrapi.com/register).
externalDocs:
description: Documentation
url: https://www.twittrapi.com/docs