Structured X (Twitter) profile and timeline data over a simple HTTP API. Every response is JSON by default; CSV, TSV and NDJSON are one parameter away.
https://data.spyderproxy.comaccess_key in the query string. Profile lookups are
also available without a key at a reduced rate limit.
| Method | Path | Purpose |
|---|---|---|
| GET | /twitter/profile | Profile details, optionally with recent posts |
| GET | /twitter/tweets | Paginated timeline |
| POST | /twitter/monitor | Watch a profile for new posts |
| GET | /twitter/monitor | List your monitors |
| GET | /twitter/monitor/:id/events | Posts a monitor has detected |
| DELETE | /twitter/monitor/:id | Stop a monitor |
| GET | /free/twitter/profile | Free No key required |
curl "https://data.spyderproxy.com/twitter/profile?access_key=YOUR_KEY&url=x.com/naval"
{
"success": true,
"data": {
"username": "@naval",
"name": "Naval",
"bio": "Incompressible",
"followers": 3893151,
"following": 0,
"tweets": 27129,
"is_verified": true,
"joined": "2007-02-01",
"location": "",
"id": "745273",
"profile_image_url": "https://pbs.twimg.com/profile_images/.../ycqwaMI2.jpg",
"banner_url": "https://pbs.twimg.com/profile_banners/745273/1588490328",
"website": "https://nav.al",
"is_protected": false
},
"meta": { "cached": false, "credits_used": 1, "credits_remaining": 9391 }
}
| Name | Default | Description |
|---|---|---|
url | required | Profile URL, @handle, or bare handle. A status URL resolves to its author. |
include | — | tweets appends recent posts at no extra cost. |
format | json | json, csv, tsv, ndjson |
fresh | false | Bypass the 15-minute cache. |
Page through a profile's posts. Use page for simple numbered
pagination — cursors are handled server-side.
curl "https://data.spyderproxy.com/twitter/tweets?access_key=YOUR_KEY&url=x.com/naval&page=1&limit=20"
{
"success": true,
"data": { "tweets": [ {
"id": "2089518337382572522",
"text": "...",
"created_at": "2026-08-18T01:02:47.000Z",
"likes": 2671, "retweets": 58, "replies": 104,
"quotes": 54, "bookmarks": 921, "views": 1228373,
"is_retweet": false, "is_pinned": false,
"url": "https://x.com/i/status/2089518337382572522"
} ] },
"meta": { "page": 1, "per_page": 20, "has_more": true, "next_page": 2 }
}
Loop until has_more is false. next_page
is provided so a client never has to compute it.
| Name | Default | Description |
|---|---|---|
page | 1 | Page number. Cannot be combined with cursor. |
limit | 20 | Posts per page, 1–100. Honoured exactly. |
include_replies | false | Include replies and reposts. |
format | json | json, csv, tsv, ndjson |
cursor | — | Advanced: raw cursor from meta.next_cursor. Never walks extra pages, so it is the cheapest way to crawl sequentially. |
Add format=csv to any endpoint to get a spreadsheet-ready file
instead of JSON. Page metadata moves to response headers
(X-Page, X-Has-More, X-Next-Page) since a
flat file has nowhere to carry it.
curl "https://data.spyderproxy.com/twitter/tweets?access_key=YOUR_KEY&url=x.com/naval&page=1&format=csv" -o naval.csv
id,text,created_at,likes,retweets,replies,quotes,bookmarks,views,is_retweet,is_pinned,url
2089273166049009797,"Don't send me the report, just send me the prompt.",2026-08-17T08:48:34.000Z,11458,620,...
Watch a profile and get notified when it posts. Creating a monitor records a baseline immediately, so you are only ever notified about posts published after you started watching.
curl -X POST "https://data.spyderproxy.com/twitter/monitor?access_key=YOUR_KEY\
&url=x.com/naval&interval=300&webhook_url=https://you.example/hook"
Each webhook is signed. Verify before trusting it:
const expected = crypto.createHmac("sha256", secret)
.update(rawBody).digest("hex");
if (expected !== req.headers["x-signature"]) return res.status(401).end();
The secret is returned only when the monitor is created. If you
would rather poll than receive webhooks, omit webhook_url and read
/twitter/monitor/:id/events. Each poll counts as one request.
Errors use standard status codes and a consistent body. You are never charged for a request that fails.
{ "success": false, "error": { "code": "not_found", "message": "..." } }
| Status | Code | Meaning |
|---|---|---|
| 400 | bad_request | Invalid handle, format or parameter combination |
| 401 | unauthorized | Missing or invalid access_key |
| 402 | insufficient_credits | Balance exhausted |
| 404 | not_found | Profile does not exist, is suspended, or is unavailable |
| 429 | rate_limited | Too many requests |
| 502 | upstream_failed | Upstream problem; retry shortly |
One request equals one credit. Deep timeline pages that require several
upstream fetches report the true cost in meta.upstream_requests and
meta.credits_used, so charges are never a surprise. Cached
responses are served in single-digit milliseconds.
Every response carries X-Credits-Used and
X-Credits-Remaining.
Data is collected from publicly visible profile pages. Private and suspended accounts are not accessible.