How Nitter Parses GraphQL Tweet Types: Handling TweetUnavailable, TweetTombstone, and Visibility Results

Nitter discriminates GraphQL tweet variants through the parseGraphTweet procedure in src/parser.nim, routing TweetUnavailable, TweetTombstone, and TweetWithVisibilityResults through specialized branches based on the js.getTypeName field while falling back to legacy parsing for standard content.

Nitter, the privacy-focused alternative Twitter frontend, consumes Twitter’s modern GraphQL API to fetch timeline data. Because Twitter represents deleted, restricted, and subscriber-only content through distinct JSON schemas, understanding how Nitter parses GraphQL tweet types is essential for developers extending or debugging the Nim-based scraper.

The Dispatch Logic in parseGraphTweet

The entry point for all GraphQL tweet processing is the parseGraphTweet procedure located in src/parser.nim. According to the source code, this function inspects the incoming JSON node’s type metadata via js.getTypeName and dispatches to dedicated handling logic using a discriminated case statement.

At lines 71–78 of src/parser.nim, the parser checks for special GraphQL tweet types before attempting heavy-weight legacy parsing. This early-exit pattern keeps the parser lightweight for edge-case tweets while preserving full feature support for ordinary content.

Handling Unavailable Content

When Twitter returns metadata indicating a tweet cannot be displayed, Nitter handles two distinct GraphQL representations: TweetUnavailable and TweetTombstone.

TweetUnavailable Nodes

If js.getTypeName returns "TweetUnavailable", the parser immediately returns a default Tweet object with the available field set to false. This indicates the tweet is inaccessible due to network issues, authentication requirements, or geographic restrictions, bypassing all media and card parsing to save resources.

TweetTombstone Nodes

For "TweetTombstone" types—representing content removed due to DMCA requests or Terms of Service violations—the parser extracts the human-readable explanation using the getTombstone helper.

Located in src/parserutils.nim at lines 165–168, getTombstone reads the "text" field from the tombstone node and strips the trailing “Learn more” suffix to normalize the message. The resulting string is placed into the Tweet object’s text field, allowing Nitter to display the removal rationale to users.

Visibility Variants and Recursion

Beyond unavailable content, Twitter’s GraphQL API wraps certain tweets in visibility envelopes that require recursive parsing.

TweetWithVisibilityResults

When encountering "TweetWithVisibilityResults", the parser recursively calls parseGraphTweet on the nested "tweet" object (js{"tweet"}). This unwraps the visibility metadata while preserving the underlying tweet content, ensuring subscriber-only or filtered tweets still render correctly when accessible.

TweetPreviewDisplay

For "TweetPreviewDisplay" types, which represent subscriber-only content viewed by non-subscribers, the parser returns a static message explaining the limited visibility. This prevents parsing errors while clearly communicating the content restriction to the frontend.

Fallback to Legacy Parsing

If the JSON node does not match any of the special GraphQL types listed above, execution falls through to the standard legacy tweet parsing logic within parseGraphTweet. This branch handles traditional tweet structures containing legacy fields, card data, media entities, and user information, ensuring backward compatibility with older API response formats.

Working with the Parser API

The following examples demonstrate how to leverage Nitter’s public parser API to handle GraphQL tweet variants directly.

Parsing a GraphQL Tweet Node

import json, parser

let gqlPayload = readFile("sample_graphql.json")
let js = parseJson(gqlPayload)

# parseGraphTweet returns a fully-populated Tweet record.

let tweet = parseGraphTweet(js)

echo tweet.text          # Contains tombstone message, unavailable placeholder, or real text.

echo tweet.available    # false for unavailable/tombstone tweets.

Extracting Tombstone Text Manually

import json, parserutils

proc formatTweet(js: JsonNode): string =
  case js.getTypeName:
  of "TweetTombstone":
    # Re-use the library’s helper for consistency.

    result = js{"tombstone"}.getTombstone
  else:
    result = js{"legacy", "full_text"}.getStr

echo formatTweet(someJsonNode)

These snippets illustrate how parseGraphTweet abstracts the complexity of TweetTombstone and TweetUnavailable handling, returning clean Tweet objects regardless of the underlying GraphQL structure.

Summary

  • Nitter routes GraphQL tweet variants through parseGraphTweet in src/parser.nim, which discriminates on js.getTypeName at lines 71–78.
  • TweetUnavailable produces a Tweet with available = false, skipping expensive parsing.
  • TweetTombstone extracts cleaned removal text via getTombstone in src/parserutils.nim (lines 165–168), stripping the “Learn more” suffix.
  • TweetWithVisibilityResults triggers recursive parsing of the nested "tweet" object to unwrap visibility envelopes.
  • Unmatched types fall through to legacy parsing, ensuring compatibility with standard tweet structures.

Frequently Asked Questions

What is the difference between TweetUnavailable and TweetTombstone in Nitter?

TweetUnavailable indicates the tweet is simply inaccessible—often due to network errors or geographic blocks—and returns an empty Tweet with available = false. TweetTombstone specifically indicates the tweet was removed by Twitter (e.g., for copyright violations) and contains a text explanation extracted by the getTombstone helper that Nitter displays to the user.

How does Nitter handle subscriber-only tweets in GraphQL responses?

When the parser encounters TweetPreviewDisplay or TweetWithVisibilityResults types, it either returns a static subscriber-only message or recursively parses the nested tweet object, respectively. This ensures the application handles restricted content gracefully without crashing on unexpected JSON schemas.

Where is the tombstone text cleaned in the Nitter codebase?

The getTombstone procedure in src/parserutils.nim (lines 165–168) handles normalization by reading the tombstone node’s "text" field and programmatically removing the trailing “Learn more” suffix that Twitter appends to removal notices.

What happens when Nitter encounters an unknown GraphQL tweet type?

If parseGraphTweet does not recognize the type name from js.getTypeName, execution falls through to the legacy parsing branch. This branch expects standard Twitter API structures containing legacy fields and extracts media, cards, and user data using the traditional parsing logic.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →