SpyderData

API Documentation

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.

Base URL
https://data.spyderproxy.com

Authentication
Pass your key as access_key in the query string. Profile lookups are also available without a key at a reduced rate limit.

Endpoints

MethodPathPurpose
GET/twitter/profileProfile details, optionally with recent posts
GET/twitter/tweetsPaginated timeline
POST/twitter/monitorWatch a profile for new posts
GET/twitter/monitorList your monitors
GET/twitter/monitor/:id/eventsPosts a monitor has detected
DELETE/twitter/monitor/:idStop a monitor
GET/free/twitter/profileFree No key required

Profile lookup

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 }
}

Parameters

NameDefaultDescription
urlrequiredProfile URL, @handle, or bare handle. A status URL resolves to its author.
includetweets appends recent posts at no extra cost.
formatjsonjson, csv, tsv, ndjson
freshfalseBypass the 15-minute cache.

Timeline

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.

Parameters

NameDefaultDescription
page1Page number. Cannot be combined with cursor.
limit20Posts per page, 1–100. Honoured exactly.
include_repliesfalseInclude replies and reposts.
formatjsonjson, csv, tsv, ndjson
cursorAdvanced: raw cursor from meta.next_cursor. Never walks extra pages, so it is the cheapest way to crawl sequentially.

Output formats

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,...

Monitoring

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

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": "..." } }
StatusCodeMeaning
400bad_requestInvalid handle, format or parameter combination
401unauthorizedMissing or invalid access_key
402insufficient_creditsBalance exhausted
404not_foundProfile does not exist, is suspended, or is unavailable
429rate_limitedToo many requests
502upstream_failedUpstream problem; retry shortly

Rate limits & billing

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.