GeoGryphon API Documentation

Programmatic access to AI visibility audits and content briefs. Available on Professional and Agency plans.

https://api.geogryphon.com

Authentication

GeoGryphon supports two auth methods. Both work on every authenticated endpoint — pick whichever fits your use case.

JWT (recommended for browsers)

Sign in with Google via POST /auth/google to receive a JWT, then include it as a Bearer token. JWTs are short-lived (7 days) and tied to a specific user.

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

API Keys (recommended for servers, Professional and Agency tiers)

Generate a key via POST /auth/api-keys, then include it in the X-API-Key header. API keys don't expire — revoke manually if compromised. Each user may have one active key at a time; generating a new one replaces the previous.

X-API-Key: geogryphon_a1b2c3d4...

All endpoints except GET /health and POST /auth/google require authentication. Requests without a valid JWT or API key will receive a 401 Unauthorized response.

Auth Endpoints

POST /auth/google No Auth Required

Exchange a Google OAuth ID token for a GeoGryphon JWT session token. Use the Google Sign-In SDK to obtain the credential value.

Request Body
{
  "credential": "eyJhbGciOiJSUzI1NiIs...google_id_token"
}
Response 200 OK
{
  "token": "eyJhbGciOiJIUzI1NiIs...geogryphon_jwt",
  "user": {
    "userId": "usr_a1b2c3d4",
    "email": "user@example.com",
    "name": "Jane Smith",
    "tier": "professional"
  }
}
GET /auth/me Auth Required

Get the current authenticated user's profile, plan tier, and usage counters for the current billing period.

Response 200 OK
{
  "userId": "usr_a1b2c3d4",
  "email": "user@example.com",
  "name": "Jane Smith",
  "tier": "professional",
  "auditsUsed": 12,
  "briefsUsed": 5,
  "resetsAt": "2026-05-01T00:00:00.000Z",
  "paymentStatus": "ok",
  "teamOwnerId": null,
  "hasStripe": true,
  "oneTimeCredits": {
    "snapshot": 0,
    "bundle": 0,
    "brief": 0,
    "agencyPack": 0
  }
}
DELETE /auth/me Auth Required

Permanently delete the authenticated user's account. Cancels any active Stripe subscription, wipes all audits, briefs, monitors, API keys, and team memberships. Agency owners also unlink their team members (who are downgraded to Free tier). GDPR/CCPA right-to-erasure compliant.

This action is irreversible. A typed confirmation is required in the request body to prevent accidental deletions.

Request Body
{
  "confirm": "DELETE"
}
Response 200 OK
{
  "message": "Account deleted",
  "deletedAt": "2026-04-21T15:30:00.000Z"
}

Returns 400 if the confirm field is missing or not exactly "DELETE".

API Key Endpoints

API keys let you authenticate server-to-server without a JWT. Professional, Business, and Agency tiers only. Each user may have one active key at a time — generating a new key revokes the previous one automatically.

POST /auth/api-keys Auth Required (JWT)

Generate a new API key. Must be authenticated with a JWT (not with an existing API key). Returns the key once — save it immediately; the full key is not stored and cannot be retrieved later.

Response 201 Created
{
  "apiKey": "geogryphon_a1b2c3d4e5f6...40hex",
  "prefix": "geogryphon_a1b2c3d4e5f",
  "message": "Save this key — it won't be shown again"
}

Returns 403 if the user's tier is below Professional.

GET /auth/api-keys Auth Required

Check whether the user has an active API key. Returns the prefix (first 22 chars, safe to display) and creation time, never the full key.

Response 200 OK (has key)
{
  "hasKey": true,
  "prefix": "geogryphon_a1b2c3d4e5f",
  "createdAt": "2026-04-10T12:00:00.000Z"
}
Response 200 OK (no key)
{
  "hasKey": false
}
DELETE /auth/api-keys Auth Required

Revoke the current API key. All subsequent requests using the revoked key will return 401 Unauthorized.

Response 200 OK
{
  "message": "API key revoked"
}

Team Endpoints

Invite up to 10 team members who sign in with their own Google accounts and inherit your subscription tier. Agency tier only. Auto-link: if you invite an email that later signs in with Google, they're automatically linked to your team on their first login — no separate invite link required.

POST /auth/team/invite Auth Required (Agency)

Invite a new team member by email. Emails are normalized to lowercase before storage, so case mismatches on future Google sign-in won't break the auto-link.

Request Body
{
  "email": "colleague@acme.com",
  "role": "member"
}
FieldTypeRequiredDescription
emailstringYesTeam member's email (RFC-compliant, max 254 chars)
rolestringYesOne of: admin, member, viewer
Response 201 Created
{
  "inviteId": "inv_a1b2c3d4e5f6...",
  "email": "colleague@acme.com",
  "role": "member",
  "status": "pending"
}
GET /auth/team Auth Required

List all team members (pending invites + active members). Team owners see all entries; team members see the same list read-only.

Response 200 OK
{
  "members": [
    {
      "email": "colleague@acme.com",
      "role": "member",
      "status": "active",
      "joinedAt": "2026-04-15T10:30:00.000Z"
    },
    {
      "email": "intern@acme.com",
      "role": "viewer",
      "status": "pending",
      "createdAt": "2026-04-20T14:00:00.000Z"
    }
  ]
}
DELETE /auth/team/{email} Auth Required (Agency)

Remove a team member. If the member had already signed in and was linked to your team, their tier is downgraded to Free on their next API call. URL-encode the email in the path.

Path Parameters
ParameterTypeDescription
emailstringURL-encoded email (e.g. colleague%40acme.com). Case-insensitive.
Response 200 OK
{
  "message": "Team member removed"
}

Audit Endpoints

POST /audits Auth Required

Run an AI visibility audit for a domain across multiple AI engines. Checks how each engine references your brand, calculates share of voice, and compares against competitors.

Request Body
{
  "domain": "acme.com",
  "keywords": ["project management", "task tracking"],
  "competitors": ["notion.so", "asana.com"]
}
FieldTypeRequiredDescription
domainstringYesThe domain to audit (e.g. "acme.com")
keywordsstring[]YesKeywords to check visibility for (max 10)
competitorsstring[]NoCompetitor domains to compare against (limit by tier)
Response 200 OK
{
  "auditId": "aud_x7k9m2n4",
  "userId": "usr_a1b2c3d4",
  "domain": "acme.com",
  "keywords": ["project management"],
  "createdAt": "2026-04-07T14:30:00.000Z",
  "engines": {
    "chatgpt": {
      "mentioned": true,
      "position": 2,
      "snippet": "Acme is a leading project management tool...",
      "citationUrl": "https://acme.com/features"
    },
    "perplexity": {
      "mentioned": true,
      "position": 1,
      "snippet": "For project management, Acme offers...",
      "citationUrl": "https://acme.com"
    }
  },
  "shareOfVoice": {
    "score": 0.42,
    "rank": 2,
    "totalCompetitors": 5
  },
  "comparison": [
    { "domain": "notion.so", "score": 0.58, "rank": 1 },
    { "domain": "acme.com", "score": 0.42, "rank": 2 }
  ]
}
GET /audits Auth Required

List your audits, ordered by most recent first. Returns up to 50 audits per request.

Response 200 OK
{
  "audits": [
    {
      "auditId": "aud_x7k9m2n4",
      "domain": "acme.com",
      "keywords": ["project management"],
      "shareOfVoice": { "score": 0.42 },
      "createdAt": "2026-04-07T14:30:00.000Z"
    }
  ],
  "count": 1
}
GET /audits/{id} Auth Required

Get the full details of a specific audit by its ID. Returns the complete audit object including all engine results and competitor comparisons.

Path Parameters
ParameterTypeDescription
idstringThe audit ID (e.g. "aud_x7k9m2n4")
Response 200 OK

Returns the full audit object (same structure as POST /audits response).

Content Brief Endpoints

POST /briefs Auth Required

Generate a citation-optimized content brief. The brief includes recommended sections, entities to mention, JSON-LD structured data, and SEO guidance tailored to a specific AI engine.

Request Body
{
  "topic": "Best project management tools",
  "keywords": ["project management", "team collaboration"],
  "domain": "acme.com",
  "targetEngine": "chatgpt"
}
FieldTypeRequiredDescription
topicstringYesThe content topic or title
keywordsstring[]YesTarget keywords (max 10)
domainstringYesYour domain for brand-specific guidance
targetEnginestringNoAI engine to optimize for: chatgpt, perplexity, gemini, claude, bing_copilot, google_aio, meta_ai, grok. Defaults to "chatgpt".
Response 200 OK
{
  "briefId": "brf_p3q8r5s1",
  "userId": "usr_a1b2c3d4",
  "topic": "Best project management tools",
  "targetEngine": "chatgpt",
  "createdAt": "2026-04-07T15:00:00.000Z",
  "sections": [
    {
      "heading": "Introduction",
      "wordCount": 150,
      "guidance": "Lead with a clear definition. Mention your brand by name in the first paragraph..."
    },
    {
      "heading": "Feature Comparison",
      "wordCount": 400,
      "guidance": "Use a structured comparison table. Include specific metrics..."
    }
  ],
  "entities": [
    { "name": "project management", "type": "concept", "importance": "high" },
    { "name": "Acme", "type": "brand", "importance": "high" }
  ],
  "jsonLd": {
    "@context": "https://schema.org",
    "@type": "Article",
    "headline": "Best project management tools",
    "author": { "@type": "Organization", "name": "Acme" }
  },
  "seoGuidance": {
    "titleTag": "Best Project Management Tools (2026) | Acme",
    "metaDescription": "Compare the best project management tools...",
    "targetWordCount": 2200,
    "readabilityLevel": "grade 8"
  }
}
GET /briefs Auth Required

List your content briefs, ordered by most recent first. Returns up to 50 briefs per request.

Response 200 OK
{
  "briefs": [
    {
      "briefId": "brf_p3q8r5s1",
      "topic": "Best project management tools",
      "targetEngine": "chatgpt",
      "createdAt": "2026-04-07T15:00:00.000Z"
    }
  ],
  "count": 1
}
GET /briefs/{id} Auth Required

Get the full details of a specific content brief by its ID.

Path Parameters
ParameterTypeDescription
idstringThe brief ID (e.g. "brf_p3q8r5s1")
Response 200 OK

Returns the full brief object (same structure as POST /briefs response).

Billing Endpoints

POST /stripe/create-checkout Auth Required

Create a Stripe Checkout session to subscribe to a paid plan. Returns a URL to redirect the user to Stripe's hosted checkout page.

Request Body
{
  "priceId": "price_1TJRSW7lGnd7R4baNEF1VmPV",
  "successUrl": "https://geogryphon.com/success.html",
  "cancelUrl": "https://geogryphon.com/pricing.html"
}
Response 200 OK
{
  "url": "https://checkout.stripe.com/c/pay/cs_live_...",
  "sessionId": "cs_live_..."
}
POST /stripe/portal Auth Required

Create a Stripe Customer Portal session. The portal lets users manage their subscription, update payment methods, and view invoices.

Request Body
{
  "returnUrl": "https://geogryphon.com/dashboard.html"
}
Response 200 OK
{
  "url": "https://billing.stripe.com/p/session/..."
}

System Endpoints

GET /health No Auth Required

Health check endpoint. Use this to verify the API is operational.

Response 200 OK
{
  "status": "ok",
  "timestamp": "2026-04-07T14:30:00.000Z"
}

Rate Limits

API requests are rate-limited per user using a sliding window of 60 seconds. When you exceed the limit, the API returns 429 Too Many Requests. The response includes a Retry-After header indicating how many seconds to wait.

Tier Audits Briefs Auth Monthly Audits Monthly Briefs
Free 5/min 3/min 10/min (IP) 3 1
Starter 15/min 10/min 10/min (IP) 25 10
Professional 30/min 20/min 10/min (IP) Unlimited Unlimited
Agency 60/min 40/min 10/min (IP) Unlimited Unlimited

Monthly usage counters reset automatically on the first day of each billing period. Check your current usage via GET /auth/me.

Code Examples

cURL — Run an AI Visibility Audit

# Run an audit for acme.com
curl -X POST https://api.geogryphon.com/audits \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -d '{
    "domain": "acme.com",
    "keywords": ["project management", "task tracking"],
    "competitors": ["notion.so", "asana.com"]
  }'

JavaScript — Fetch Audit Results

// Run an audit and retrieve results
const response = await fetch('https://api.geogryphon.com/audits', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    domain: 'acme.com',
    keywords: ['project management'],
    competitors: ['notion.so']
  })
});

const audit = await response.json();
console.log(`Share of Voice: ${audit.shareOfVoice.score * 100}%`);
console.log(`Engines checked: ${Object.keys(audit.engines).length}`);

Python — Generate a Content Brief

import requests

API_BASE = "https://api.geogryphon.com"
TOKEN = "YOUR_JWT_TOKEN"

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {TOKEN}"
}

# Generate a content brief optimized for ChatGPT
brief = requests.post(
    f"{API_BASE}/briefs",
    headers=headers,
    json={
        "topic": "Best project management tools",
        "keywords": ["project management"],
        "domain": "acme.com",
        "targetEngine": "chatgpt"
    }
).json()

print(f"Brief ID: {brief['briefId']}")
print(f"Sections: {len(brief['sections'])}")
print(f"Entities: {len(brief['entities'])}")

Error Codes

All error responses follow a consistent format:

{
  "error": "Short error code",
  "message": "Human-readable description of what went wrong."
}
StatusErrorDescription
400 bad_request Missing or invalid request parameters. Check the request body matches the expected schema.
401 unauthorized Missing or invalid JWT token. Re-authenticate via POST /auth/google to obtain a new token.
403 forbidden Your plan does not include access to this feature. Upgrade at pricing.
404 not_found The requested resource (audit, brief, etc.) does not exist or belongs to another user.
429 rate_limited Too many requests. Wait for the duration specified in the Retry-After header before retrying.
500 internal_error An unexpected server error occurred. If this persists, contact support.