Skip to main content

🐍 airanks — the AIR Python SDK

PyPI-ready python license typed deps

What is AIR? AIR (Artificial Intelligence Ranking) by airanks makes AI optimization visible — a 0–10 score for how often, and how well, an AI assistant like ChatGPT cites a given domain when answering real questions. Look up any site's AIR score at airanks.net, or install the browser toolbar to see it while you browse.

airanks is the Python door into that data: one client class, three methods, one dependency. No CLI, no scaffolding — pip install, import, go. 🚀


📚 Table of contents


📦 Install

pip install airanks

Requires Python 3.9+. One runtime dependency: requests. Ships a py.typed marker, so your type checker sees real annotations, not Any soup.

📥 Installing from source (until the PyPI release lands)
git clone https://git.shoemoney.ai/shoemoney/airanks-oss.git
cd airanks-oss/python-sdk
pip install -e .

⚡ Quickstart

🔑 Every method now requires a token — a free account gets you one at airanks.net/tokens. Set it via AIR_API_KEY (or pass api_key= below) before calling anything; see Shared authentication.

import os
from airanks import AirClient

os.environ["AIR_API_KEY"] = "your-token-here"  # or export it in your shell
client = AirClient()

domain = client.domain("stripe.com")
print(domain["data"]["air_score"])  # 0-10

results = client.search("payment processing")
who = client.user()  # raises ApiError(401) if the token is missing, invalid, or revoked

A fuller example with error handling lives in examples/lookup.py — run it with python examples/lookup.py stripe.com.


🔌 API reference

AirClient is the whole surface area. Three methods, all GET, all JSON in and out:

Method Returns Notes
client.domain(host: str) -> dict {"data": {...domain}, "meta": {"dataset_version": ...}} AIR score, percentile, and AI-file posture (llms.txt, ai.txt, robots.txt AI-agent rules, JSON-LD) for a hostname. Always 200s for a valid host — a never-before-seen domain triggers server-side hydration, so data["ai_files"]["status"] == "pending" means "check again shortly," not an error.
client.search(query: str) -> dict {"data": {"domains": [], "brands": [], "phrases": []}, "meta": {...}} Matches across everything AIR tracks.
client.user() -> dict The authenticated user (name, email) for whichever token was resolved. Raises ApiError with status_code == 401 if the token is missing, invalid, or revoked.

Constructor:

AirClient(api_key: str | None = None, api_base: str | None = None)
Arg Default Effect
api_key None → shared resolution order An explicit key takes priority over everything and always attaches, same as an env-sourced token.
api_base AIR_API_BASE env, else https://airanks.net/api/v1 Point at staging, a mirror, or a local dev server.

🔐 Shared authentication

A free account is required. Anonymous requests now get a 401 authentication_required — grab a token at airanks.net/tokens, then set it via AIR_API_KEY or pass api_key= to AirClient(). One login works across every AIR client: run air login once from the air CLI or the browser toolbar, and this SDK picks up the same token — no separate config, no re-auth.

Resolution order (first hit wins), identical across every AIR client — the air CLI, the browser toolbar, and every other language SDK in the ecosystem:

Priority Source Behavior
1️⃣ AIR_API_KEY env var Explicit intent — always attaches, to any host.
2️⃣ ~/.config/air/auth.json The file air login writes. Host-scoped: only attaches to requests aimed at the host it was saved for, so a repointed AIR_API_BASE can't accidentally leak a token elsewhere.
3️⃣ Anonymous No token attached — the API now rejects these with 401 authentication_required. Every method requires a token from one of the two sources above (or an explicit api_key=).

Skip all of that and use a key unconditionally:

client = AirClient(api_key="your-token-here")

🧭 How auth resolution works

flowchart TD
    Start(["AirClient() constructed"]) --> Explicit{"api_key passed\nto constructor?"}
    Explicit -- yes --> UseExplicit["source = explicit\nalways attaches"]
    Explicit -- no --> Env{"AIR_API_KEY\nenv var set?"}
    Env -- yes --> UseEnv["source = env\nalways attaches"]
    Env -- no --> File{"~/.config/air/auth.json\nreadable + has token?"}
    File -- yes --> UseFile["source = file\nattaches ONLY if\nrequest host == saved host"]
    File -- no --> Anon["source = anonymous\nno Authorization header\n→ 401 authentication_required"]

    UseExplicit --> Request["client._get(url)"]
    UseEnv --> Request
    UseFile --> HostCheck{"urlparse(url).hostname\n== saved host?"}
    Anon --> Request
    HostCheck -- yes --> Attach["Authorization: Bearer <token>"]
    HostCheck -- no --> NoAttach["request sent unauthenticated\n→ 401 authentication_required"]
    Attach --> Request
    NoAttach --> Request

🚨 Error handling

Non-2xx responses (and transport failures — timeouts, DNS errors) raise airanks.ApiError:

from airanks import AirClient, ApiError

client = AirClient()
try:
    domain = client.domain("example.com")
except ApiError as e:
    if e.status_code == 429:
        time.sleep(e.retry_after or 30)
Attribute Type Meaning
status_code int | None HTTP status. None means a transport-level failure, not an HTTP response.
retry_after int | None Present on a 429 when the server sends a Retry-After header — seconds to wait.

The error message is pulled from the response body ({"error": {"message": ...}} or the 422 shape {"message": ...}), falling back to a generic "API returned {status_code}".


🧪 Testing

tests/test_auth.py covers token-resolution order and the host-scoped attach rule — no network calls, fully offline:

pip install -e ".[dev]"   # or: pip install -e . pytest
pytest

🌐 The AIR ecosystem

airanks (this package) is one door into AIR. Same API, same shared auth, different language:

Client What it is
🖥️ go-cli Zero-dependency Go CLI
🖥️ node-cli Node.js CLI
🦀 rust-cli Rust CLI
🐘 composer-package PHP client
📜 js-sdk JS/TS SDK for Node & browser
🧰 chrome-extension The AIR browser toolbar
🔌 mcp-server MCP server — air_rank / air_files / air_search tools for any agent
🤖 agent-toolkit Universal AI-agent toolkit (Claude, Codex, Cursor, Cline, …)

Why AIR: search used to be the whole game — now the traffic that matters is an AI assistant deciding whether to cite you at all, and that's a different AI optimization problem than classic SEO. AIR exists to make that measurable, with a 0–10 score backed by real observed citations, not a self-reported checklist. This client returns the same numbers airanks.net and the browser toolbar show — available from Python.


📄 License

MIT — see LICENSE.

Made with 🐍 + ☕ for anyone who wants their AI Rank without leaving Python.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

airanks-1.0.1.tar.gz (9.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

airanks-1.0.1-py3-none-any.whl (10.1 kB view details)

Uploaded Python 3

File details

Details for the file airanks-1.0.1.tar.gz.

File metadata

  • Download URL: airanks-1.0.1.tar.gz
  • Upload date:
  • Size: 9.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for airanks-1.0.1.tar.gz
Algorithm Hash digest
SHA256 771cfe56fc0593687d70f4080128bdf48554181178963aed553c290cdfafe288
MD5 e2f6e74d5951332119ab7bd6d9ff6590
BLAKE2b-256 6d4db81665e2683ade0b2925bc1297b1a032f233ccff0cd9932b1277091ffcf5

See more details on using hashes here.

Provenance

The following attestation bundles were made for airanks-1.0.1.tar.gz:

Publisher: release.yml on airanks-net/python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file airanks-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: airanks-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 10.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for airanks-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ba1655751fa659680c39733d14433721aecdf59e8a4f9db5a1ecf224d61e30da
MD5 c5c9e238ae351938008d477dba3a2185
BLAKE2b-256 3242c2953e7ea13bce584c3b875d386e6a745a69281a0bf6e2c4c686a99c94c5

See more details on using hashes here.

Provenance

The following attestation bundles were made for airanks-1.0.1-py3-none-any.whl:

Publisher: release.yml on airanks-net/python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page