{
  "openapi": "3.1.0",
  "info": {
    "title": "carbium-tokens · Solana Token Metadata API",
    "version": "1.0.0",
    "summary": "Search, lookup, batch-resolve and proxy logos for ~2M Solana SPL & Token-2022 mints.",
    "description": "Public REST API for Solana token metadata, part of the Carbium ecosystem. Free, no auth, CORS-permissive. The registry is kept fresh on Carbium's RPC via Yellowstone gRPC + Metaplex/Token-2022 enrichment. Rate limits are per-IP sliding windows; 429 responses carry a `Retry-After` header. Human reference: https://tokens.carbium.io/endpoints",
    "contact": {
      "name": "Carbium",
      "url": "https://carbium.io"
    },
    "x-pricing": "free"
  },
  "servers": [
    { "url": "https://tokens.carbium.io", "description": "Production" }
  ],
  "externalDocs": {
    "description": "Human-readable API reference",
    "url": "https://tokens.carbium.io/endpoints"
  },
  "tags": [
    { "name": "tokens", "description": "Token metadata search, lookup and batch resolve" },
    { "name": "images", "description": "Cached token logo proxy" }
  ],
  "paths": {
    "/tokens": {
      "get": {
        "tags": ["tokens"],
        "operationId": "searchTokens",
        "summary": "Search the token registry",
        "description": "Search by symbol, name, or mint. An empty query returns top-by-volume. Ranking: exact mint → exact symbol → symbol-prefix → name-prefix → fuzzy. Rate limit: 60 req/min/IP.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Text query: mint, symbol, or partial name. Omit or leave empty to get top tokens by volume.",
            "schema": { "type": "string" }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Results per page.",
            "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-indexed page number.",
            "schema": { "type": "integer", "minimum": 1, "default": 1 }
          },
          {
            "name": "include_token22",
            "in": "query",
            "required": false,
            "description": "Set to false to hide Token-2022 mints.",
            "schema": { "type": "boolean", "default": true }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of matching tokens.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SearchResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/tokens/{mint}": {
      "get": {
        "tags": ["tokens"],
        "operationId": "getToken",
        "summary": "Look up a single mint",
        "description": "Returns the token row for one mint. Responds 404 when the mint is not yet in the registry. Rate limit: 180 req/min/IP.",
        "parameters": [
          {
            "name": "mint",
            "in": "path",
            "required": true,
            "description": "Base58 SPL or Token-2022 mint address.",
            "schema": { "type": "string" },
            "example": "So11111111111111111111111111111111111111112"
          }
        ],
        "responses": {
          "200": {
            "description": "The token row.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Token" }
              }
            }
          },
          "404": {
            "description": "Mint not in the registry.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/tokens/batch": {
      "post": {
        "tags": ["tokens"],
        "operationId": "batchResolveTokens",
        "summary": "Batch-resolve up to 500 mints",
        "description": "Resolve up to 500 mints in one request. Known mints are returned in `items`; unknown mints are returned in `unknown` and auto-enqueued for on-chain enrichment. Rate limit: 20 req/min/IP.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/BatchRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resolved tokens plus the list of unknown mints.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BatchResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/img/{mint}": {
      "get": {
        "tags": ["images"],
        "operationId": "getTokenImage",
        "summary": "Cached token logo proxy",
        "description": "Returns a cached logo image for the mint. ALWAYS responds 200 with an image, falling back to an 'Unknown' SVG when the upstream logo is unreachable, so an <img> tag never breaks. Unknown mints are silently enqueued for resolution. Response is cached server-side after the first hit. Rate limit: 600 req/min/IP.",
        "parameters": [
          {
            "name": "mint",
            "in": "path",
            "required": true,
            "description": "Base58 mint address.",
            "schema": { "type": "string" },
            "example": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
          }
        ],
        "responses": {
          "200": {
            "description": "Token logo (PNG/SVG/etc). Never 404s.",
            "content": {
              "image/*": {
                "schema": { "type": "string", "format": "binary" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "responses": {
      "RateLimited": {
        "description": "Per-IP rate limit exceeded. Includes a `Retry-After` header. Global fallback across all routes is 300 req/min/IP.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying.",
            "schema": { "type": "integer" }
          }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      }
    },
    "schemas": {
      "Token": {
        "type": "object",
        "description": "A single token row. The shape returned by /tokens (items), /tokens/{mint}, and /tokens/batch (items).",
        "properties": {
          "mint": { "type": "string", "description": "Base58 mint address.", "example": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" },
          "symbol": { "type": "string", "example": "USDC" },
          "name": { "type": "string", "example": "USD Coin" },
          "decimals": { "type": "integer", "example": 6 },
          "logo_url": { "type": ["string", "null"], "format": "uri" },
          "logo_status": { "type": "string", "enum": ["ok", "unchecked", "broken", "missing"] },
          "is_token22": { "type": "boolean" },
          "daily_volume_usd": { "type": ["number", "null"] },
          "first_seen_at": { "type": "integer", "description": "Epoch milliseconds." },
          "last_seen_at": { "type": "integer", "description": "Epoch milliseconds." },
          "supply": { "type": ["string", "null"], "description": "Raw supply as a string (may exceed JS safe integer range)." },
          "mint_authority": { "type": ["string", "null"] },
          "freeze_authority": { "type": ["string", "null"] },
          "metaplex_uri": { "type": ["string", "null"], "format": "uri" },
          "update_authority": { "type": ["string", "null"] },
          "seller_fee_basis_points": { "type": ["integer", "null"] },
          "token_standard": { "type": ["integer", "null"], "description": "Metaplex TokenStandard enum value." },
          "description": { "type": ["string", "null"] },
          "external_url": { "type": ["string", "null"], "format": "uri" },
          "extensions": {
            "type": ["object", "null"],
            "description": "Free-form links (website, twitter, telegram, …).",
            "additionalProperties": { "type": "string" }
          },
          "tags": { "type": ["array", "null"], "items": { "type": "string" } }
        },
        "required": ["mint", "symbol", "name", "decimals", "logo_status", "is_token22"]
      },
      "SearchResponse": {
        "type": "object",
        "properties": {
          "items": { "type": "array", "items": { "$ref": "#/components/schemas/Token" } },
          "page": { "type": "integer", "description": "1-indexed page returned." },
          "limit": { "type": "integer", "description": "Page size used." },
          "total": { "type": "integer", "description": "Total matches across all pages." }
        },
        "required": ["items", "page", "limit", "total"]
      },
      "BatchRequest": {
        "type": "object",
        "properties": {
          "mints": {
            "type": "array",
            "description": "Up to 500 base58 mint addresses.",
            "items": { "type": "string" },
            "minItems": 1,
            "maxItems": 500
          }
        },
        "required": ["mints"]
      },
      "BatchResponse": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "description": "Resolved token rows for known mints.",
            "items": { "$ref": "#/components/schemas/Token" }
          },
          "unknown": {
            "type": "array",
            "description": "Mints not in the registry (auto-enqueued for enrichment).",
            "items": { "type": "string" }
          }
        },
        "required": ["items", "unknown"]
      },
      "Error": {
        "type": "object",
        "properties": {
          "statusCode": { "type": "integer" },
          "error": { "type": "string" },
          "message": { "type": "string" }
        }
      }
    }
  }
}
