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
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.
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
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
/data still takes these URLs: as on any site without a dedicated parser, an AI model learns the page's structure (the first request for a new kind of page can take up to about 45 seconds) and returns its key values, priced per site rather than at the flat 15 credits (see the API reference). If no structured data can be found, you get a 422 that isn't charged.
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.3.1
// 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.
The key fields per page type. Names are snake_case across every site; the full contract is in the API reference.
type: "tweet"| Field | What it holds |
|---|---|
id, url, text, lang, created_at | The 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_count | Every counter X shows. view_count is null on posts from before X counted views (late 2022). |
is_reply, is_quote, is_retweet, is_edited | Post kind flags, plus in_reply_to_id, in_reply_to_username and conversation_id. |
quoted_tweet, parent_tweet, retweeted_tweet | The related post, one level deep: the same fields, with its own related posts null. |
hashtags, urls, user_mentions, symbols | Entities: hashtags, expanded links, mentioned accounts, cashtags. |
media | Photos and videos with type, url (the best MP4 for video), preview_url, size, duration_ms, alt_text. |
card, place | Link preview card (title, url, image_url…) and tagged place. |
author | username, name, description, is_verified, is_blue_verified, followers, following, statuses_count. |
replies | The 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"| Field | What it holds |
|---|---|
id, username, name, url, description | Identity and bio. |
location, external_url, profile_picture, cover_picture, created_at | Profile details and join date. |
is_verified, is_blue_verified, verified_type, is_private | Verification and protection. |
followers, following, statuses_count, affiliates_count | Account counts; affiliates_count is for organizations. |
pinned_tweet_ids, tweets | Pinned 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. |
Everything comes from what Twitter (X) shows a logged-out visitor. That sets these limits.
favourites_count, media_count and listed_count come back null./ai/fields extracts the fields you describe in plain language from any public page, and /html returns the rendered HTML for your own parser.
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.
| Plan | Price | Credits | Pages |
|---|---|---|---|
| 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.
422, not charged.500, not charged.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.webscraping-ai has a data command; pass - to read URLs from stdin, one per line.
webscraping-ai data 'https://x.com/jack/status/20'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.
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.
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. Fetch failures return a 500 and aren't charged. Pages outside the supported Twitter (X) page types go to the AI-learned path and are priced per site. parse_failed and not_found results are successful requests and are charged.
What happens if I send a URL the Twitter (X) parser doesn't cover?
/data still answers it: an AI model learns the page's structure (the first request for a new kind of page can take up to about 45 seconds) and returns its key values, priced per site like any site without a dedicated parser. If no structured data can be found, you get a 422 that isn't charged; /ai/fields then extracts the fields you name, and /html returns the rendered page for your own parser.
One API key, one request format, the same response envelope.
Get started with 2,000 free API credits. No credit card required.