# How Nitter Generates and Executes Twitter GraphQL API Calls

> Discover how Nitter generates and executes Twitter GraphQL API calls. Learn about its variable construction, authentication methods, and robust request execution with automatic retries for efficient Twitter data access.

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

---

**Nitter generates Twitter GraphQL API calls by constructing JSON-encoded variables in `src/api.nim`, applying OAuth or cookie-based authentication headers in `src/apiutils.nim`, and executing asynchronous HTTP requests with automatic retry logic for rate-limit handling.**

Nitter, the privacy-focused Twitter front-end written in Nim, bypasses official API restrictions by interfacing directly with Twitter's private GraphQL endpoints. Understanding how Nitter generates and executes Twitter GraphQL API calls requires examining the three-stage pipeline implemented in the source code: request construction, header preparation, and response execution.

## Constructing GraphQL Requests in src/api.nim

The foundation of Nitter's Twitter interaction begins in `src/api.nim`, where the application builds structured `ApiReq` objects containing GraphQL operation names and serialized variables.

### Building Parameters with genParams

The `genParams` procedure (lines 7‑13) assembles the query string parameters required for every GraphQL request. It accepts a JSON-encoded **variables** string and optional **fieldToggles**, returning a sequence of key-value pairs that include the static `gqlFeatures` payload defined in `src/consts.nim`.

```nim
proc genParams(variables: string; fieldToggles = ""): seq[(string, string)] =
  result.add ("variables", variables)
  result.add ("features", gqlFeatures)
  if fieldToggles.len > 0:
    result.add ("fieldToggles", fieldToggles)

```

### Creating API URLs with apiUrl and apiReq

The `apiUrl` function (lines 14‑15) wraps the parameters into an `ApiUrl` object, while `apiReq` (lines 17‑19) duplicates this structure for both authentication methods, returning an `ApiReq` record containing separate URL configurations for cookie and OAuth sessions.

```nim
proc apiUrl(endpoint, variables: string; fieldToggles = ""; skipTid = false): ApiUrl =
  return ApiUrl(endpoint: endpoint, params: genParams(variables, fieldToggles), skipTid: skipTid)

proc apiReq(endpoint, variables: string; fieldToggles = ""; skipTid = false): ApiReq =
  let url = apiUrl(endpoint, variables, fieldToggles, skipTid)
  return ApiReq(cookie: url, oauth: url)

```

### Example: Fetching User Tweets

Concrete endpoint helpers like `userTweetsUrl` demonstrate this pattern in action. Located at line 33 in `src/api.nim`, this function constructs a request for the `graphUserTweetsV2` endpoint using the `restIdVars` template string and `userTweetsFieldToggles` constant (both defined in `src/consts.nim`).

```nim
proc userTweetsUrl(id: string; cursor: string): ApiReq =
  return apiReq(graphUserTweetsV2, restIdVars % [id, cursor, "20"], userTweetsFieldToggles)

```

## Preparing Authentication Headers in src/apiutils.nim

Once the base request structure exists, `src/apiutils.nim` handles HTTP header generation and URL resolution, selecting appropriate authentication credentials based on the active session type.

### Generating Base Headers with genHeaders

The `genHeaders` procedure (lines 81‑92) establishes common headers including `accept`, `user-agent`, and `x-twitter-client-language` for every outgoing request.

### OAuth vs Cookie-Based Authentication

Lines 94‑112 implement the authentication selection logic. For **OAuth** sessions, Nitter adds an `Authorization` header generated by `getOauthHeader`. For **cookie-based** sessions, the code injects `x-twitter-auth-type`, `x-csrf-token`, and `cookie` headers, along with a `referer`. The logic also determines whether to use the standard `bearerToken` or the short-lived `bearerToken2` based on the `skipTid` flag.

### Transaction ID Generation

When required, Nitter generates a unique **x-client-transaction-id** header by calling `genTid` (lines 8‑13), which creates a UUID for transaction tracking. The `skipTid` field in `ApiUrl` controls whether this header is omitted for specific endpoints.

### Resolving Final URLs

The `toUrl` function (lines 51‑60) transforms an `ApiReq` into a complete `Uri`, selecting the appropriate base domain (`https://api.x.com` for OAuth or `https://x.com/i/api` for cookies) and prepending the `graphql/` path prefix unless the target is a legacy `1.1/` REST endpoint.

## Executing Requests and Handling Responses

The actual network layer implements resilient communication patterns to handle Twitter's rate limiting and error responses.

### The fetch and fetchImpl Procedures

The public `fetch` procedure (lines 29‑44) in `src/apiutils.nim` returns a parsed `JsonNode` and wraps all requests in retry logic. The `fetchImpl` template (lines 30‑88) performs the underlying HTTP GET using a shared `HttpPool`, decompresses gzip-encoded responses, detects Cloudflare HTML error pages, and parses JSON error objects from the `"errors"` field. When encountering recoverable errors, it invalidates the session and raises a `RateLimitError` to trigger the retry mechanism.

```nim

# Conceptual usage of the fetch API

let session = await getSession(cookieReq)
let jsonResponse = await fetch(session, apiRequest)

```

### Automatic Retry Logic for Rate Limits

The `retry` template (lines 5‑28) implements exponential backoff for failed requests. It attempts the operation up to `maxRetries` times, waiting `retryDelayMs` milliseconds between attempts. This ensures Nitter gracefully handles temporary failures and HTTP 429 rate-limit responses without dropping user requests.

For endpoints requiring raw string responses (such as media URLs), `fetchRaw` (lines 50‑58) provides a variant that bypasses JSON parsing and returns the uncompressed payload directly.

## Complete API Call Flow Example

A typical timeline retrieval demonstrates the full pipeline:

```nim

# 1. Resolve user ID via UserByScreenName GraphQL endpoint

let user = await getGraphUser("example")

# 2. Request tweets using the UserTweets GraphQL operation

let tweets = await getGraphUserTweets(user.id, TimelineKind.tweets)

```

Internally, `getGraphUser` calls `userUrl` to build the request, `fetchRaw` retrieves the JSON, and `parseGraphUser` (in `src/parser.nim`) converts the response into a Nim `User` object. Similarly, `getGraphUserTweets` selects `userTweetsUrl`, constructs the `ApiReq`, and passes it through `fetch` with the current session's authentication headers.

## Summary

- Nitter constructs GraphQL requests in `src/api.nim` using `genParams`, `apiUrl`, and `apiReq` to build structured `ApiReq` objects containing endpoint names and JSON variables.
- Authentication headers are generated in `src/apiutils.nim` via `genHeaders`, supporting both OAuth (`Authorization` header) and cookie-based sessions (`x-csrf-token`, `cookie`).
- The `x-client-transaction-id` header is dynamically generated by `genTid` when required by the endpoint configuration.
- Network execution uses `fetch` and `fetchImpl` with a connection pool (`HttpPool`), gzip decompression, and automatic retry logic that handles rate limits through the `retry` template.
- Endpoint constants and variable templates reside in `src/consts.nim`, while response parsing occurs in `src/parser.nim` and `src/parserutils.nim`.

## Frequently Asked Questions

### What authentication methods does Nitter use for Twitter GraphQL API calls?

Nitter supports two authentication methods implemented in `src/apiutils.nim`: **OAuth 1.0a** sessions that use a calculated `Authorization` header via `getOauthHeader`, and **cookie-based** sessions that transmit `x-twitter-auth-type`, `x-csrf-token`, and `cookie` headers. The system maintains parallel `ApiUrl` structures for both methods within the `ApiReq` type and selects the appropriate headers based on the active session kind.

### How does Nitter handle Twitter rate limits when making GraphQL requests?

Nitter implements automatic retry logic through the `retry` template in `src/apiutils.nim` (lines 5‑28). When `fetchImpl` detects a rate-limit response or Cloudflare error page, it raises a `RateLimitError` that triggers the retry mechanism. The system attempts the request up to `maxRetries` times with a configurable `retryDelayMs` delay between attempts, ensuring temporary blocks do not terminally fail user requests.

### What is the purpose of the x-client-transaction-id header in Nitter's API requests?

The **x-client-transaction-id** header serves as a unique transaction identifier generated by the `genTid` procedure in `src/apiutils.nim`. Twitter's API uses this UUID to track individual request flows and detect anomalous traffic patterns. Nitter conditionally includes this header based on the `skipTid` flag in the `ApiUrl` object, omitting it only for specific legacy endpoints or when using alternative authentication flows that require `bearerToken2`.

### Which Nim modules are responsible for Twitter GraphQL request generation in Nitter?

The primary modules involved are `src/api.nim` (constructing GraphQL variables and `ApiReq` objects), `src/consts.nim` (storing endpoint constants like `graphUserTweetsV2` and JSON templates), and `src/apiutils.nim` (generating headers, resolving URLs, and executing HTTP requests). Response parsing is handled by `src/parser.nim` and `src/parserutils.nim`, while `src/http_pool.nim` manages the underlying connection pooling for performance optimization.