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

> Discover how Nitter parses GraphQL tweet types like TweetUnavailable and TweetTombstone. Learn about specialized parsing branches and legacy fallbacks in src/parser.nim.

- Repository: [Zed/nitter](https://github.com/zedeus/nitter)
- Tags: internals
- Published: 2026-08-29

---

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

```nim
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

```nim
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.