The official JavaScript / TypeScript SDK for the AIR API — programmatic access to AI Rank data for Node and the browser. Three methods, zero runtime dependencies, ships its own types.
AIR (Artificial Intelligence Ranking) by airanks makes AI optimization visible: how often, and how well, AI assistants like ChatGPT cite a given domain when answering real questions. It's a 0–10 score per domain, backed by real observed citations, not a self-reported checklist.
Look up any site's AIR score at airanks.net, or install the airanks toolbar to see it while you browse.
This package is the JS/TS door into that same data — no CLI, no scaffolding, just a class with three methods. 🚪
- What is AIR?
- Install
- Quick Start
- How it works
- Methods
- Auth — shared across every AIR client
- Errors
- TypeScript
- The
airfamily - License
npm install @airanks-net/sdkRequires Node 18+ (for global fetch) server-side; any modern browser client-side.
⚠️ Every request needs a token — get a free one at airanks.net/tokens, then setAIR_API_KEY(Node) or passapiKey(browser). See Auth below.
import { AirClient } from '@airanks-net/sdk';
// Node: reads AIR_API_KEY, or ~/.config/air/auth.json written by `air login`.
const client = new AirClient();
const { data: domain } = await client.domain('stripe.com');
console.log(domain.air_score); // 0-10
const { data: results } = await client.search('payment processing');
const who = await client.user(); // throws ApiError(401) if unauthenticatedSee examples/lookup.mjs for a fuller example with error handling —
run it with npm run build && node examples/lookup.mjs.
Request & auth flow (click to expand) 🖱️
sequenceDiagram
autonumber
participant App as Your code
participant SDK as AirClient
participant Auth as auth.ts
participant API as api.airanks.net
App->>SDK: new AirClient()
SDK->>Auth: resolve token (Node only)
Auth-->>SDK: AIR_API_KEY env, then ~/.config/air/auth.json, else anonymous
App->>SDK: client.domain('stripe.com')
SDK->>API: GET /v1/domains/stripe.com
alt first-ever lookup for this host
API-->>SDK: 200, ai_files.status = "pending"
loop poll (honors 429 Retry-After) until pollMaxMs
SDK->>API: GET /v1/domains/stripe.com
API-->>SDK: 200, still pending…
end
API-->>SDK: 200, ai_files.status = "ready"
else already hydrated
API-->>SDK: 200, ai_files.status = "ready"
end
SDK-->>App: { data, meta }
domain() always 200s for a valid hostname. A never-before-seen domain triggers server-side
hydration behind the scenes, so the SDK polls automatically while ai_files.status === "pending",
honoring 429 Retry-After along the way. If the poll budget (pollMaxMs, default 180s) runs out
while still pending, it resolves with pendingAtCap: true instead of throwing — treat air_score
as unknown, not a real zero, in that case.
| Method | Returns | Notes |
|---|---|---|
domain(host, options?) |
{ data, meta, pendingAtCap? } |
AIR score, percentile, and AI-file posture (llms.txt, ai.txt, robots.txt AI-agent rules, JSON-LD) for a hostname. Auto-polls while hydrating. options.pollMs (default 20s) and options.pollMaxMs (default 180s) are tunable. |
search(query) |
{ data: { domains: [], brands: [], phrases: [] }, meta } |
Matches across everything AIR tracks. |
user() |
AirUser ({ name, email }) |
The authenticated user for whichever token was resolved. Throws ApiError with status === 401 if the token is missing, invalid, or revoked. |
Resolution order (first hit wins), identical to every other AIR client — the air CLI and the
browser toolbar included — so logging in once with any of them authenticates this SDK too:
flowchart LR
A["🔑 AIR_API_KEY env var\n(Node only, explicit intent)"] -->|found| T["Attach Bearer token"]
A -->|not set| B["📄 ~/.config/air/auth.json\n(written by `air login`)"]
B -->|found, host matches| T
B -->|not found| C["🚫 Anonymous\n(401 authentication_required)"]
AIR_API_KEYenv var (Node only) — explicit intent, always attaches.~/.config/air/auth.json(Node only) — the fileair loginwrites. A token loaded from here only attaches to requests aimed at the host it was saved for, so a repointedapiBasecan't accidentally leak it elsewhere.- Anonymous — no token, request rejected: the API returns
401witherror.code === "authentication_required"(the message includes the signup URL).
ℹ️ One login, every client.
AIR_API_KEYenv >~/.config/air/auth.json> anonymous — the same three-step resolution runs in this SDK, theairCLI, and the browser toolbar, so logging in once works everywhere. There is no working anonymous fallback anymore — every caller except the official browser toolbar needs a token.
🔑 A free account is required. Every request through this SDK needs a token — grab one at airanks.net/tokens, then set it via
AIR_API_KEY(Node) or pass it explicitly asapiKeyto the client constructor (browser, or to override Node's resolved token).
In the browser, this SDK never reads env vars or touches disk — pass a token explicitly:
const client = new AirClient({ apiKey: 'your-air-token' });Point at a different API base (staging, a mirror, etc.) with AIR_API_BASE (Node) or the
apiBase constructor option:
const client = new AirClient({ apiBase: 'https://staging.airanks.net/api/v1' });Non-2xx responses reject with ApiError, which carries the HTTP status (.status) and, for a
429, the server's Retry-After seconds (.retryAfter) when present:
import { AirClient, ApiError } from '@airanks-net/sdk';
try {
const { data } = await client.domain('example.com');
} catch (err) {
if (err instanceof ApiError && err.status === 429) {
// domain() already retries 429s internally up to its poll budget — this only
// fires if that budget is exhausted while still throttled.
}
}Ships its own .d.ts types — Domain, AiFiles, SearchResults, AirUser, ApiError, and
more are exported from the package root:
| Export | Kind |
|---|---|
AirClient |
class |
ApiError |
class |
AirClientOptions, DomainOptions |
types |
Domain, AiFiles, ResponseMeta |
types |
DomainResponse, SearchResponse, SearchResults, SearchHit |
types |
AirUser |
type |
Both ESM (import) and CommonJS (require) builds are published; pick either without
configuration. Full contract details live in API-CONTRACT.md at the repo
root — the source of truth every air client (this SDK, the Node/Rust/Go CLIs, and the PHP
Composer package) implements identically.
This SDK is one client in the airanks-net open-source family, all speaking the same API contract and sharing the same login:
| Client | What it is |
|---|---|
node-cli |
Reference air CLI implementation (Node) |
rust-cli |
air CLI in Rust |
go-cli |
air CLI in Go |
python-sdk |
Python SDK |
composer-package |
PHP/Composer package |
mcp-server |
Model Context Protocol server — AIR for agents |
chrome-extension |
The airanks toolbar |
homebrew-tap |
brew install for the CLIs |
MIT — see LICENSE.
Built for AI optimization by the folks at airanks 🟩 · one score, every AI · airanks.net