twitterapi.io is an independent third-party service. Not affiliated with X Corp.

Blogtwitter metadata

Twitter (X) Tweet Metadata — API Fields Reference

By Sarah Wong4 min read

The Twitter (X) tweet object returned by APIs is a rich structured record — dozens of fields covering the tweet's content, engagement, entities, geolocation, threading, and context. Devs building on the API need to know what's available without digging through docs.x.com endpoint by endpoint.

This page is the practical fields reference — actual response shape, what each field means, when it's populated vs null. Pricing references URL-cited.

01 — Section

The core fields — always populated

id: string, unique tweet identifier. Persistent across the tweet's lifetime.

text: string, the actual tweet content. Up to 280 characters for standard posts.

author_id: string, ID of the posting account. Combine with /twitter/user/info for full profile data.

created_at: ISO-8601 timestamp. UTC.

conversation_id: string, the root tweet ID of the thread this belongs to. Same as id for standalone tweets.

lang: string, language code (ISO 639-1) — the platform's automated language detection.

02 — Section

public_metrics — the engagement scoreboard

Populated when you request the public_metrics field or use twitterapi.io's default response (includes public_metrics by default).

like_count: int, number of likes.

retweet_count: int, number of retweets.

reply_count: int, number of replies.

quote_count: int, number of quote-tweets.

bookmark_count (X official only): int, added by X in 2023, may be null on older data.

03 — Section

entities — the parsed content

Contains extracted structured entities from the tweet text.

hashtags: list of {start, end, tag} objects. tag is the hashtag without the # prefix.

mentions: list of {start, end, username, id} objects for @ mentions.

urls: list of {start, end, url, expanded_url, display_url, title, description} for URLs. expanded_url is the target after t.co redirect.

cashtags: list of {start, end, tag} for stock cashtags like $TSLA.

annotations (X official): entity-recognition results (people, places, products) — Twitter's NER pass on the text.

04 — Section

referenced_tweets — the threading + amplification pointers

Present when the tweet is a reply, quote, or retweet.

type: string, one of "replied_to", "quoted", "retweeted".

id: string, the referenced tweet's ID.

For a reply: type="replied_to", id = parent tweet.

For a quote tweet: type="quoted", id = quoted tweet.

For a retweet: type="retweeted", id = original tweet.

05 — Section

attachments — media + polls

media_keys: list of media key strings. Use with media_fields expansion for full media metadata (dimensions, URLs, alt text).

poll_ids: list of poll ID strings. Use with poll_fields expansion for poll options + vote counts.

06 — Section

geo — location data

Present when the tweet was posted with location tagging (opt-in per user).

place_id: X place identifier.

coordinates: {type: "Point", coordinates: [lon, lat]} for tweets with precise coordinates.

Most tweets don't have geo populated — location-tagging is opt-in and uncommon.

07 — Section

Runnable — parsing the full metadata

Fetch a tweet + inspect every populated field:

python
import os, requests, json

HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"

def inspect_tweet(tweet_id: str):
    r = requests.get(
        f"{BASE}/twitter/tweet",
        headers=HEADERS, params={"tweet_id": tweet_id}, timeout=10,
    )
    r.raise_for_status()
    tweet = r.json()
    print("Core fields:")
    for k in ["id", "created_at", "conversation_id", "lang", "author"]:
        print(f"  {k}: {tweet.get(k, 'not set')}")
    pm = tweet.get("public_metrics", {})
    print("public_metrics:", json.dumps(pm, indent=2))
    entities = tweet.get("entities", {})
    print("entities:")
    for k in ["hashtags", "mentions", "urls", "cashtags"]:
        vals = entities.get(k, [])
        print(f"  {k}: {len(vals)} found")
    print("referenced_tweets:", tweet.get("referenced_tweets", "none"))
    return tweet

inspect_tweet("1234567890")
08 — Section

Field-availability matrix — twitterapi.io vs X official

Both providers return the same tweet object structure — differences are in defaults + tier gating.

Fieldtwitterapi.ioX official (Basic tier)X official (Pro/Enterprise)
id, text, author, created_at✓ always✓ always✓ always
public_metrics✓ default✓ requires tweet.fields=public_metrics✓ default
entities✓ default✓ requires expansion✓ default
referenced_tweets✓ when applicable✓ requires expansion✓ default
geo✓ when populated✓ requires geo.place_id field✓ default
context annotationslimited✓ requires field✓ default
non_public_metrics❌ owner-account only✓ owner-account only

Two practical observations: (a) non_public_metrics (impressions, profile clicks) is only accessible for the tweet's owner via OAuth user-context — no third-party API can see it; (b) twitterapi.io's default response includes what X official gates behind explicit field requests, which simplifies dev-side handling; per-tweet cost ratio ~33× at $0.00015 vs $0.005 per cited pricing.

09 — Section

Common metadata workflows

Content classification / analysis: use text + lang + entities for NLP pipelines. entities.hashtags for topic mapping, entities.urls for external-reference tracking.

Engagement analysis: public_metrics across a tweet corpus. Compute engagement-rate = (like_count + retweet_count + reply_count) / follower_count from author profile.

Threading / conversation trace: conversation_id + referenced_tweets reconstruct the reply chain. Query all tweets with matching conversation_id for the full thread.

Deletion detection: periodically re-query stored tweet IDs. 404 responses indicate deletion.

Deduplication of retweets: filter on referenced_tweets[].type == "retweeted" and dedupe by the referenced ID to get only original posts.

python
# Practical example: build a per-tweet metadata table for analysis.
import os, requests, json
from pathlib import Path

HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"

def extract_metadata(tweet: dict) -> dict:
    entities = tweet.get("entities", {})
    pm = tweet.get("public_metrics", {})
    refs = tweet.get("referenced_tweets", [])
    return {
        "id": tweet.get("id"),
        "created_at": tweet.get("created_at"),
        "lang": tweet.get("lang"),
        "text_length": len(tweet.get("text", "")),
        "hashtag_count": len(entities.get("hashtags", [])),
        "url_count": len(entities.get("urls", [])),
        "mention_count": len(entities.get("mentions", [])),
        "is_reply": any(r.get("type") == "replied_to" for r in refs),
        "is_quote": any(r.get("type") == "quoted" for r in refs),
        "is_retweet": any(r.get("type") == "retweeted" for r in refs),
        "like_count": pm.get("like_count", 0),
        "retweet_count": pm.get("retweet_count", 0),
        "reply_count": pm.get("reply_count", 0),
        "has_media": bool(tweet.get("attachments", {}).get("media_keys")),
        "has_poll": bool(tweet.get("attachments", {}).get("poll_ids")),
    }

# Search query returning multiple tweets → extract metadata → write to JSONL for analysis
r = requests.get(
    f"{BASE}/twitter/tweet/advanced_search",
    headers=HEADERS,
    params={"query": '"machine learning" min_faves:100 lang:en'},
    timeout=15,
)
r.raise_for_status()

out = Path("metadata.jsonl")
with open(out, "w") as f:
    for tweet in r.json().get("tweets", []):
        meta = extract_metadata(tweet)
        f.write(json.dumps(meta) + "\n")

print(f"wrote {out} with metadata rows")

# Cost per twitterapi.io/pricing:
#   ~500 tweets × $0.00015 = $0.075 per full search
#   All 20+ metadata fields included — no extra cost per field
10 — Questions

Questions readers ask

Do all tweets have geo data?

No — geo-tagging is opt-in per user and most users don't enable it. Most tweets have geo absent or null.

What's the difference between public_metrics and non_public_metrics?

public_metrics is visible to everyone via the API (likes, retweets, replies, quotes). non_public_metrics (impressions, profile clicks, URL clicks) is only accessible for the tweet's owner via OAuth user-context — no third-party API can see it for other users' tweets.

How do I get media URLs from an attachment?

The media_keys field returns keys; use the media_fields expansion or a separate /media endpoint to resolve to full media URLs, dimensions, alt-text. twitterapi.io simplifies this by including media details in the default response.

Can I get the tweet's URL from the metadata?

Not returned as a field, but constructible: https://x.com//status/. Combine author.userName with id to build the URL client-side.

Why do some fields sometimes return as null?

Field-specific reasons: entities is empty when the tweet has no hashtags/URLs/mentions; referenced_tweets is empty when not a reply/quote/retweet; geo is null when the poster didn't opt into location tagging. Null is a legitimate state, not an error.

Does twitterapi.io return additional fields not in X official's schema?

Response shape closely mirrors X's canonical schema for compatibility. Some helpful conveniences (e.g., media URLs inlined by default) but no exotic non-standard fields — portability across providers is preserved.

11 — Further reading

Continue

Sources & further reading
More from this series
Build it

Stop reading. Start building.

Starter credits cover real testing on real data. Google sign-in, no card, no application queue.

Get an API key
    Twitter (X) Metadata — API Reference | TwitterAPI.io