How Nitter Generates and Executes Twitter GraphQL API Calls
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.
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.
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).
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.
# 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:
# 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.nimusinggenParams,apiUrl, andapiReqto build structuredApiReqobjects containing endpoint names and JSON variables. - Authentication headers are generated in
src/apiutils.nimviagenHeaders, supporting both OAuth (Authorizationheader) and cookie-based sessions (x-csrf-token,cookie). - The
x-client-transaction-idheader is dynamically generated bygenTidwhen required by the endpoint configuration. - Network execution uses
fetchandfetchImplwith a connection pool (HttpPool), gzip decompression, and automatic retry logic that handles rate limits through theretrytemplate. - Endpoint constants and variable templates reside in
src/consts.nim, while response parsing occurs insrc/parser.nimandsrc/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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →