# How Nitter Fetches a Single Tweet Conversation Using the GraphQL API

> Discover how Nitter fetches single tweet conversations using the GraphQL API. Learn about constructing requests and parsing JSON responses to reconstruct tweet threads.

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

---

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

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

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

```nim

# 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:

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