STRUCTURED DATA API

Twitter (X) Scraper API

A Twitter scraper you call over HTTP: send an X (formerly Twitter) post or profile URL to /data and get the text, every engagement counter including views, media, and author stats back as JSON. No X developer account or API tier. Flat 15 credits per page.

2,000 free API credits · No credit card required

Scrape tweets and X profiles

Send the page's normal URL to /data. The page type is detected from the URL, and the response tells you which one it was in request_parameters.type.

Post (tweet)

type: "tweet"

Full text (long posts included), likes, replies, reposts, quotes, bookmarks and views, hashtags, links, mentions, media, link cards, the quoted or parent post, the author, and the few top replies X shows logged-out visitors.

x.com/user/status/POST_ID twitter.com/user/status/POST_ID x.com/i/status/POST_ID

Profile

type: "profile"

Name, bio, location, website, avatar and banner, join date, verification, followers, following and post count, pinned post ids and a handful of the account's top original posts.

x.com/user twitter.com/user
These URLs return a 400 that isn't charged, with a message listing what is supported: search, hashtag and explore pages, lists and communities, and a profile's replies, media or likes tabs. More page types and sites are added over time, so don't validate URLs on your side — send the URL and handle the 400.

How to scrape Twitter (X) with one API call

No selectors, proxies, or headless browser to run. The page-scraping options (js, proxy, headers…) don't apply; country picks the proxy country (default us).

curl -G "https://api.webscraping.ai/data" \
  --data-urlencode "api_key=YOUR_API_KEY" \
  --data-urlencode "url=https://x.com/jack/status/20"
# Response (excerpt):
# {
#   "request_parameters": {"url": "https://x.com/jack/status/20",
#                          "provider": "twitter", "type": "tweet"},
#   "parse_status": "ok",
#   "data": {
#     "id": "20",
#     "url": "https://x.com/jack/status/20",
#     "text": "just setting up my twttr",
#     "lang": "en",
#     "created_at": "2006-03-21T20:50:14.000Z",
#     "like_count": 290000,
#     "reply_count": 17000,
#     "retweet_count": 120000,
#     "quote_count": 25000,
#     "bookmark_count": 3000,
#     "view_count": null,
#     "media": [],
#     "author": {"id": "12", "username": "jack", "followers": 6000000, ...},
#     "replies": [{"id": "...", "text": "...", "like_count": 1200, ...}, ...],
#     ...
#   }
# }
# pip install webscraping_ai
# https://pypi.org/project/webscraping-ai/
from webscraping_ai import Client

client = Client(api_key="YOUR_API_KEY")
result = client.data("https://x.com/jack/status/20")
print(result["request_parameters"]["provider"], result["parse_status"])
print(result["data"])
# Response (excerpt):
# {
#   "request_parameters": {"url": "https://x.com/jack/status/20",
#                          "provider": "twitter", "type": "tweet"},
#   "parse_status": "ok",
#   "data": {
#     "id": "20",
#     "url": "https://x.com/jack/status/20",
#     "text": "just setting up my twttr",
#     "lang": "en",
#     "created_at": "2006-03-21T20:50:14.000Z",
#     "like_count": 290000,
#     "reply_count": 17000,
#     "retweet_count": 120000,
#     "quote_count": 25000,
#     "bookmark_count": 3000,
#     "view_count": null,
#     "media": [],
#     "author": {"id": "12", "username": "jack", "followers": 6000000, ...},
#     "replies": [{"id": "...", "text": "...", "like_count": 1200, ...}, ...],
#     ...
#   }
# }
// npm install webscraping-ai
// https://www.npmjs.com/package/webscraping-ai
import { WebScrapingAI } from 'webscraping-ai';

const client = new WebScrapingAI({ apiKey: 'YOUR_API_KEY' });
const result = await client.data({ url: 'https://x.com/jack/status/20' });
console.log(result.request_parameters.provider, result.parse_status);
console.log(result.data);
// Response (excerpt):
// {
//   "request_parameters": {"url": "https://x.com/jack/status/20",
//                          "provider": "twitter", "type": "tweet"},
//   "parse_status": "ok",
//   "data": {
//     "id": "20",
//     "url": "https://x.com/jack/status/20",
//     "text": "just setting up my twttr",
//     "lang": "en",
//     "created_at": "2006-03-21T20:50:14.000Z",
//     "like_count": 290000,
//     "reply_count": 17000,
//     "retweet_count": 120000,
//     "quote_count": 25000,
//     "bookmark_count": 3000,
//     "view_count": null,
//     "media": [],
//     "author": {"id": "12", "username": "jack", "followers": 6000000, ...},
//     "replies": [{"id": "...", "text": "...", "like_count": 1200, ...}, ...],
//     ...
//   }
// }
<?php
// composer require webscraping-ai/webscraping-ai-php
// https://packagist.org/packages/webscraping-ai/webscraping-ai-php
require 'vendor/autoload.php';

use WebScrapingAI\Client;

$client = new Client('YOUR_API_KEY');
$result = $client->data(url: 'https://x.com/jack/status/20');
echo $result['request_parameters']['provider'], ' ', $result['parse_status'], "\n";
print_r($result['data']);
// Response (excerpt):
// {
//   "request_parameters": {"url": "https://x.com/jack/status/20",
//                          "provider": "twitter", "type": "tweet"},
//   "parse_status": "ok",
//   "data": {
//     "id": "20",
//     "url": "https://x.com/jack/status/20",
//     "text": "just setting up my twttr",
//     "lang": "en",
//     "created_at": "2006-03-21T20:50:14.000Z",
//     "like_count": 290000,
//     "reply_count": 17000,
//     "retweet_count": 120000,
//     "quote_count": 25000,
//     "bookmark_count": 3000,
//     "view_count": null,
//     "media": [],
//     "author": {"id": "12", "username": "jack", "followers": 6000000, ...},
//     "replies": [{"id": "...", "text": "...", "like_count": 1200, ...}, ...],
//     ...
//   }
// }
# gem install webscraping_ai
# https://rubygems.org/gems/webscraping_ai
require 'webscraping_ai'

client = WebScrapingAI::Client.new(api_key: 'YOUR_API_KEY')
result = client.data('https://x.com/jack/status/20')
puts "#{result['request_parameters']['provider']} #{result['parse_status']}"
puts result['data'].inspect
# Response (excerpt):
# {
#   "request_parameters": {"url": "https://x.com/jack/status/20",
#                          "provider": "twitter", "type": "tweet"},
#   "parse_status": "ok",
#   "data": {
#     "id": "20",
#     "url": "https://x.com/jack/status/20",
#     "text": "just setting up my twttr",
#     "lang": "en",
#     "created_at": "2006-03-21T20:50:14.000Z",
#     "like_count": 290000,
#     "reply_count": 17000,
#     "retweet_count": 120000,
#     "quote_count": 25000,
#     "bookmark_count": 3000,
#     "view_count": null,
#     "media": [],
#     "author": {"id": "12", "username": "jack", "followers": 6000000, ...},
#     "replies": [{"id": "...", "text": "...", "like_count": 1200, ...}, ...],
#     ...
#   }
# }
// go get github.com/webscraping-ai/webscraping-ai-go/v4
// https://pkg.go.dev/github.com/webscraping-ai/webscraping-ai-go/v4
package main

import (
    "context"
    "fmt"

    webscrapingai "github.com/webscraping-ai/webscraping-ai-go/v4"
)

func main() {
    client, _ := webscrapingai.NewClient(&webscrapingai.Config{APIKey: "YOUR_API_KEY"})
    res, _ := client.Data(context.Background(), &webscrapingai.DataOptions{
        URL: "https://x.com/jack/status/20",
    })
    fmt.Println(res.RequestParameters.Provider, res.ParseStatus)
    fmt.Println(string(res.Data)) // raw JSON; its shape depends on the provider and page type
}
// Response (excerpt):
// {
//   "request_parameters": {"url": "https://x.com/jack/status/20",
//                          "provider": "twitter", "type": "tweet"},
//   "parse_status": "ok",
//   "data": {
//     "id": "20",
//     "url": "https://x.com/jack/status/20",
//     "text": "just setting up my twttr",
//     "lang": "en",
//     "created_at": "2006-03-21T20:50:14.000Z",
//     "like_count": 290000,
//     "reply_count": 17000,
//     "retweet_count": 120000,
//     "quote_count": 25000,
//     "bookmark_count": 3000,
//     "view_count": null,
//     "media": [],
//     "author": {"id": "12", "username": "jack", "followers": 6000000, ...},
//     "replies": [{"id": "...", "text": "...", "like_count": 1200, ...}, ...],
//     ...
//   }
// }
// Maven: ai.webscraping:webscraping-ai:4.2.0
// https://central.sonatype.com/artifact/ai.webscraping/webscraping-ai
import ai.webscraping.Client;
import ai.webscraping.Config;
import ai.webscraping.option.DataOptions;
import ai.webscraping.result.DataResult;

Client client = new Client(Config.builder().apiKey("YOUR_API_KEY").build());
DataResult result = client.data(DataOptions.builder()
    .url("https://x.com/jack/status/20")
    .build());
System.out.println(result.getRequestParameters().getProvider() + " " + result.getParseStatus());
System.out.println(result.getData());
// Response (excerpt):
// {
//   "request_parameters": {"url": "https://x.com/jack/status/20",
//                          "provider": "twitter", "type": "tweet"},
//   "parse_status": "ok",
//   "data": {
//     "id": "20",
//     "url": "https://x.com/jack/status/20",
//     "text": "just setting up my twttr",
//     "lang": "en",
//     "created_at": "2006-03-21T20:50:14.000Z",
//     "like_count": 290000,
//     "reply_count": 17000,
//     "retweet_count": 120000,
//     "quote_count": 25000,
//     "bookmark_count": 3000,
//     "view_count": null,
//     "media": [],
//     "author": {"id": "12", "username": "jack", "followers": 6000000, ...},
//     "replies": [{"id": "...", "text": "...", "like_count": 1200, ...}, ...],
//     ...
//   }
// }
// dotnet add package WebScrapingAI
// https://www.nuget.org/packages/WebScrapingAI
using WebScrapingAI;

var client = new WebScrapingAIClient(new WebScrapingAIClientOptions { ApiKey = "YOUR_API_KEY" });
var result = await client.DataAsync(new DataRequest {
    Url = "https://x.com/jack/status/20",
});
Console.WriteLine($"{result.RequestParameters.Provider} {result.ParseStatus}");
Console.WriteLine(result.Data);
// Response (excerpt):
// {
//   "request_parameters": {"url": "https://x.com/jack/status/20",
//                          "provider": "twitter", "type": "tweet"},
//   "parse_status": "ok",
//   "data": {
//     "id": "20",
//     "url": "https://x.com/jack/status/20",
//     "text": "just setting up my twttr",
//     "lang": "en",
//     "created_at": "2006-03-21T20:50:14.000Z",
//     "like_count": 290000,
//     "reply_count": 17000,
//     "retweet_count": 120000,
//     "quote_count": 25000,
//     "bookmark_count": 3000,
//     "view_count": null,
//     "media": [],
//     "author": {"id": "12", "username": "jack", "followers": 6000000, ...},
//     "replies": [{"id": "...", "text": "...", "like_count": 1200, ...}, ...],
//     ...
//   }
// }

Trimmed, and the counts are illustrative — they change constantly. Fields a page doesn't expose come back as null, though some flags come back false and lists come back empty. data can be null when parse_status is parse_failed or not_found, so check it before reading fields.

What the Twitter (X) scraper returns

The key fields per page type. Names are snake_case across every site; the full contract is in the API reference.

type: "tweet"

FieldWhat it holds
id, url, text, lang, created_atThe post. text is the full text, long posts included; is_truncated is true in the rare case a long post's full text wasn't on the page.
like_count, reply_count, retweet_count, quote_count, bookmark_count, view_countEvery counter X shows. view_count is null on posts from before X counted views (late 2022).
is_reply, is_quote, is_retweet, is_editedPost kind flags, plus in_reply_to_id, in_reply_to_username and conversation_id.
quoted_tweet, parent_tweet, retweeted_tweetThe related post, one level deep: the same fields, with its own related posts null.
hashtags, urls, user_mentions, symbolsEntities: hashtags, expanded links, mentioned accounts, cashtags.
mediaPhotos and videos with type, url (the best MP4 for video), preview_url, size, duration_ms, alt_text.
card, placeLink preview card (title, url, image_url…) and tagged place.
authorusername, name, description, is_verified, is_blue_verified, followers, following, statuses_count.
repliesThe few top replies X shows logged-out visitors under the post (usually 3–4), each with the post fields except the nested quoted_tweet/parent_tweet/retweeted_tweet (null) and no replies of its own.

type: "profile"

FieldWhat it holds
id, username, name, url, descriptionIdentity and bio.
location, external_url, profile_picture, cover_picture, created_atProfile details and join date.
is_verified, is_blue_verified, verified_type, is_privateVerification and protection.
followers, following, statuses_count, affiliates_countAccount counts; affiliates_count is for organizations.
pinned_tweet_ids, tweetsPinned post ids, and the handful of top original posts X shows logged-out visitors (about 5, ranked by X, not newest first), with the same one-level-deep post fields as replies.

What's not included

Everything comes from what Twitter (X) shows a logged-out visitor. That sets these limits.

Short timelines. A profile carries only the handful of top original posts X picks for logged-out visitors, not the full or latest history; replies under a post are the few top replies X shows, not the whole thread.
Some profile counts aren't public. favourites_count, media_count and listed_count come back null.
No search. Search, hashtag, list and community pages return the free 400.
Need a page type or site that isn't supported? /ai/fields extracts the fields you describe in plain language from any public page, and /html returns the rendered HTML for your own parser.

Flat pricing: 15 credits per page

Every supported Twitter (X) page type costs the same, with no extra charge for proxies or retries. That's about $1.74 per 1,000 pages on the $29 plan.

PlanPriceCreditsPages
Free $0 2,000 up to 133
Personal $29/mo 250,000 up to 16,666
Plus $99/mo 1,000,000 up to 66,666
Startup $249/mo 3,000,000 up to 200,000

The free plan's 2,000 credits renew monthly. All plans, including pay-as-you-go credits.

What's charged

Unsupported URL or page type: a 400, not charged.
Page couldn't be fetched: a 500, not charged.
Charged even without data: parse_status parse_failed (the page was fetched but couldn't be parsed) and not_found (the page doesn't exist) are successful requests and cost 15 credits, like ok.

Also from the terminal, AI agents, and n8n

CLI

webscraping-ai has a data command; pass - to read URLs from stdin, one per line.

webscraping-ai data 'https://x.com/jack/status/20'

MCP server

Claude, Cursor and other MCP clients get a webscraping_ai_data tool from the hosted MCP server (OAuth login, no API key to paste). Ask for a Twitter (X) page's data in plain language and the agent calls it.

n8n

The WebScraping.AI n8n node has a Get Structured Data operation: feed it URLs from a sheet or a trigger and route the JSON onward, no code.

Frequently asked questions

Can you still scrape data from Twitter?

Yes, from public pages. X still serves post and profile pages to logged-out visitors, and /data turns them into JSON: the full post text, every counter including views, media, the author and the top replies, or a profile with its counts and a handful of its top posts.

Do I need an X (Twitter) API plan?

No. /data reads the public post or profile page for the URL you send, so there is no X developer account, API tier or bearer token involved. You pay the same 15 credits per page whatever the post.

Does it return view counts and reposts?

Yes: like_count, reply_count, retweet_count, quote_count, bookmark_count and view_count are all returned. view_count is null only on posts older than X's view counting (late 2022) or where the count is hidden.

Can I get all posts from an account?

Not today. A profile returns the account details, pinned post ids and the handful of top original posts X shows logged-out visitors, which aren't the latest ones. For a specific post, send its URL.

Is there a free Twitter (X) scraper API?

Yes, for trying it out: the free plan includes 2,000 API credits every month, which covers 133 Twitter (X) pages at 15 credits each. No credit card is needed. Paid plans start at $29/mo for 250,000 credits, or buy pay-as-you-go credits with no subscription from a $20 top-up (100,000 credits, about 6,666 pages).

How much does the Twitter (X) Scraper API cost?

A flat 15 credits per page, for every supported Twitter (X) page type. On the $29/mo plan (250,000 credits) that is up to 16,666 pages, or about $1.74 per 1,000. Unsupported URLs return a 400 and fetch failures a 500; neither is charged. parse_failed and not_found results are successful requests and are charged.

What happens if I send a URL you don't support?

You get a 400 that isn't charged, and its message lists what is supported. For pages /data doesn't cover, /ai/fields extracts the fields you describe from any public URL, and /html returns the rendered page for your own parser.

Other sites on the same endpoint

One API key, one request format, the same response envelope.

YouTube Scraper API TikTok Scraper API LinkedIn Scraper API Instagram Scraper API Reddit Scraper API Google SERP API

Get Twitter (X) data as JSON today

Get started with 2,000 free API credits. No credit card required.

Icon