How Nitter Fetches a Single Tweet Conversation Using the GraphQL API

Nitter retrieves single tweet conversations by constructing a GraphQL request to Twitter’s internal TweetDetail endpoint, then parsing the threaded_conversation_with_injections_v2 field from the JSON response to rebuild the thread.

Nitter, the open-source alternative Twitter front-end written in Nim, bypasses JavaScript-heavy interfaces by directly querying Twitter’s internal GraphQL endpoints. Understanding how Nitter fetches a single tweet conversation using the GraphQL API reveals the lightweight architecture that enables fast, privacy-respecting tweet viewing without requiring user authentication.

Building the GraphQL Request

The conversation retrieval starts in src/api.nim where the tweetDetailUrl procedure assembles the GraphQL variables required by Twitter’s private API.

The tweetDetailUrl Procedure

Located at lines 47-53 in src/api.nim, this function injects the tweet ID and cursor into a template to generate an ApiReq object:

proc tweetDetailUrl(id, cursor: string; mode = Relevance): ApiReq =
  return apiReq(graphTweet, tweetVars % [id, cursor, $mode])

The procedure relies on two constants defined in src/consts.nim:

  • graphTweet – The GraphQL operation name (typically "TweetDetail")
  • tweetVars – A JSON template string: {"tweetId":"<id>","cursor":"<cursor>","mode":"<mode>"}

The % operator performs string interpolation, substituting the actual tweet ID, pagination cursor, and display mode (Relevance or Conversation) into the template. When the mode parameter is set to Conversation, Nitter requests the full reply thread rather than algorithmic highlights.

Executing the HTTP Request

Once the ApiReq object is built, fetchRaw in src/api.nim handles the network layer by orchestrating session management and header generation.

Session Management and Header Generation

Before issuing the request, fetchRaw calls getAndValidateSession from src/apiutils.nim to ensure the request includes valid authentication cookies. The utility then transforms the ApiReq into a complete URL using toUrl, prepending the GraphQL endpoint prefix and adding required headers via genHeaders:

let js = await fetchRaw(tweetDetailUrl(tweetId, "", Conversation))

The actual HTTP GET request executes through a shared HttpPool connection pool, returning the raw JSON body for parsing. This architecture allows Nitter to reuse connections across multiple requests while maintaining the session state required by Twitter’s API.

Parsing the Conversation Thread

The JSON response processing occurs in src/parser.nim, specifically around lines 770-800, where the parser targets the threaded_conversation_with_injections_v2 field nested within the data object.

Extracting Reply Chains

The parser navigates the instruction tree to extract individual tweets and their hierarchical relationships:


# In parser.nim (excerpt)

let thread = js{"data","threaded_conversation_with_injections_v2","instructions"}

# iterate over entries, building the conversation model

By walking through the instructions array, the parser identifies the root tweet via conversation_id_str, then recursively collects replies to reconstruct the conversation thread. The result is populated into a Conversation object defined in src/types.nim, which contains the original tweet and a sequence of reply tweets.

Complete Implementation Example

Here is the end-to-end flow for fetching a conversation in Nitter’s Nim implementation:

import api, types

let tweetId = "1651234567890123456"

# 1. Build the GraphQL request

let apiReq = tweetDetailUrl(tweetId, "", Conversation)

# 2. Execute the request with session management

let jsonResponse = await fetchRaw(apiReq)

# 3. Parse into a Conversation object

let conversation = parseConversation(jsonResponse)

# Access the tweets

echo conversation.tweet.id  # Original tweet

for reply in conversation.replies:
  echo reply.id  # Each reply in the thread

For external clients consuming Nitter’s API, the equivalent request structure mirrors this internal logic, targeting the conversation endpoint with the tweet ID as the primary parameter.

Summary

  • tweetDetailUrl in src/api.nim constructs the GraphQL request by interpolating tweet IDs into the tweetVars template from src/consts.nim.
  • fetchRaw manages HTTP execution through pooled connections, utilizing src/apiutils.nim for session validation and header generation.
  • src/parser.nim extracts conversation data from the threaded_conversation_with_injections_v2 field to rebuild reply threads.
  • The Conversation type in src/types.nim provides the structured data model for the complete tweet thread.

Frequently Asked Questions

What GraphQL endpoint does Nitter use for tweet conversations?

Nitter uses Twitter’s internal TweetDetail GraphQL operation, referenced as graphTweet in src/consts.nim. This endpoint accepts variables for tweet ID, cursor, and display mode to return both the target tweet and its associated replies.

How does Nitter handle pagination for long conversations?

The tweetDetailUrl procedure accepts a cursor parameter that extracts the next_cursor value from previous responses. When fetching subsequent pages, Nitter passes this cursor string into the GraphQL variables, allowing retrieval of older replies in the thread without reloading the entire conversation.

Where does Nitter store the GraphQL query templates?

All GraphQL operation names and variable templates reside in src/consts.nim. This includes graphTweet (the endpoint identifier), tweetVars (the JSON template for request variables), and feature toggles that enable specific Twitter API capabilities required for conversation rendering.

Why does the parser target threaded_conversation_with_injections_v2?

Twitter’s API returns conversation data within the threaded_conversation_with_injections_v2 field to accommodate injected content like promoted tweets or highlights. Nitter’s parser in src/parser.nim specifically targets this field because it contains the complete instruction set needed to reconstruct the chronological reply tree, including nested conversations and quoted tweet references.

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 →