{
  "openapi": "3.1.0",
  "info": {
    "title": "Creatordex API",
    "description": "Search the live TikTok and Instagram creator catalog by natural language. Pull full profiles, expand shortlists from a single anchor handle, and get engagement data, content samples, and (on paid tiers) contact emails. Same index and same key power both this REST API and the @creatordex/mcp MCP server. Free tier: 100 calls/month.",
    "version": "1.0.0",
    "contact": {
      "email": "support@creatordex.ai"
    },
    "x-mcp": {
      "package": "@creatordex/mcp",
      "command": "npx -y @creatordex/mcp",
      "discovery": "https://creatordex.ai/.well-known/agents.json"
    }
  },
  "servers": [
    {
      "url": "https://creatordex.ai",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "API key from the /developers page. Free tier: 100 calls/month. Paid tiers add contact emails and higher caps."
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "description": "Your monthly call cap. Omitted for uncapped/admin keys.",
        "schema": { "type": "integer" }
      },
      "X-RateLimit-Remaining": {
        "description": "Calls left this month after this request. Omitted for uncapped/admin keys.",
        "schema": { "type": "integer" }
      },
      "X-RateLimit-Reset": {
        "description": "ISO 8601 timestamp when the monthly cap resets (1st of next month, UTC).",
        "schema": { "type": "string", "format": "date-time" }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Uniform error envelope across every endpoint.",
        "properties": {
          "error": { "type": "string", "description": "Human-readable message." },
          "code": {
            "type": "string",
            "description": "Stable machine-readable error code.",
            "enum": [
              "missing_api_key",
              "invalid_api_key",
              "key_deactivated",
              "monthly_cap_reached",
              "bad_request",
              "not_found",
              "server_error"
            ]
          }
        },
        "required": ["error", "code"]
      },
      "Pagination": {
        "type": "object",
        "description": "Present on list endpoints. Results are drawn from a bounded relevance-ranked pool, so very deep offsets return an empty page with has_more=false.",
        "properties": {
          "limit": { "type": "integer", "example": 15 },
          "offset": { "type": "integer", "example": 0 },
          "count": { "type": "integer", "description": "Results on this page.", "example": 15 },
          "has_more": { "type": "boolean", "description": "Whether a further page exists.", "example": true }
        }
      },
      "Creator": {
        "type": "object",
        "description": "Search-result shape (the fields returned by GET /api/v1/search).",
        "required": ["creator_id", "handle", "platform", "last_refreshed_at"],
        "properties": {
          "creator_id": {
            "type": "string",
            "format": "uuid",
            "description": "Permanent Creatordex creator identifier. Persist this instead of the handle."
          },
          "handle": { "type": "string", "example": "jenz_losangeles" },
          "platform": { "type": "string", "enum": ["tiktok", "instagram"], "example": "tiktok" },
          "display_name": { "type": "string", "example": "Jen Z | LA Food & Fun" },
          "bio": { "type": "string" },
          "follower_count": { "type": "integer", "example": 163800 },
          "engagement_rate": { "type": "number", "example": 8.2 },
          "primary_niche": { "type": "string", "example": "food" },
          "secondary_niches": { "type": "array", "items": { "type": "string" } },
          "location_city": { "type": ["string", "null"], "example": "Los Angeles" },
          "location_country": { "type": ["string", "null"], "example": "United States" },
          "profile_image_url": { "type": ["string", "null"], "format": "uri" },
          "sample_content_thumbnails": { "type": ["array", "null"], "items": { "type": "string", "format": "uri" } },
          "contact_email": {
            "type": ["string", "null"],
            "format": "email",
            "description": "Paid tiers only. Always null on the Free tier."
          },
          "instagram_handle": { "type": ["string", "null"] },
          "twitter_handle": { "type": ["string", "null"] },
          "website": { "type": ["string", "null"], "format": "uri" },
          "last_refreshed_at": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "When Creatordex last refreshed this creator profile. Null means freshness is unavailable."
          }
        }
      },
      "CreatorDetail": {
        "type": "object",
        "description": "Full profile shape (GET /api/v1/creators/{handle} and items in /similar). Superset of Creator. recent_video_stats is a freeform object whose shape may evolve.",
        "additionalProperties": true,
        "required": ["creator_id", "handle", "platform", "last_refreshed_at"],
        "properties": {
          "creator_id": {
            "type": "string",
            "format": "uuid",
            "description": "Permanent Creatordex creator identifier. Persist this instead of the handle."
          },
          "handle": { "type": "string" },
          "platform": { "type": "string", "enum": ["tiktok", "instagram"] },
          "display_name": { "type": "string" },
          "bio": { "type": "string" },
          "follower_count": { "type": "integer" },
          "following_count": { "type": ["integer", "null"] },
          "engagement_rate": { "type": "number" },
          "follower_tier": { "type": ["string", "null"], "example": "micro" },
          "primary_niche": { "type": "string" },
          "secondary_niches": { "type": "array", "items": { "type": "string" } },
          "content_format_tags": { "type": "array", "items": { "type": "string" } },
          "location_city": { "type": ["string", "null"] },
          "location_country": { "type": ["string", "null"] },
          "profile_image_url": { "type": ["string", "null"], "format": "uri" },
          "sample_content_thumbnails": { "type": ["array", "null"], "items": { "type": "string", "format": "uri" } },
          "sample_content_urls": { "type": ["array", "null"], "items": { "type": "string", "format": "uri" } },
          "contact_email": {
            "type": ["string", "null"],
            "format": "email",
            "description": "Paid tiers only. Always null on the Free tier."
          },
          "instagram_handle": { "type": ["string", "null"] },
          "twitter_handle": { "type": ["string", "null"] },
          "youtube_handle": { "type": ["string", "null"] },
          "website": { "type": ["string", "null"], "format": "uri" },
          "is_verified": { "type": "boolean" },
          "posts_per_week": { "type": ["number", "null"] },
          "last_posted_at": { "type": ["string", "null"], "format": "date-time" },
          "last_refreshed_at": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "When Creatordex last refreshed this creator profile."
          },
          "avg_likes_per_post": { "type": ["integer", "null"] },
          "avg_comments_per_post": { "type": ["integer", "null"] },
          "brand_safety_score": { "type": ["number", "null"] },
          "overall_score": { "type": ["number", "null"] },
          "recent_video_stats": { "type": ["object", "null"], "additionalProperties": true }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Forbidden": {
        "description": "The API key has been deactivated.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "BadRequest": {
        "description": "Invalid request parameters.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Resource not found.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "CapReached": {
        "description": "Monthly call cap reached. Body carries the reset date and an upgrade URL; status 402 (Payment Required) because it clears by upgrading, not by waiting.",
        "headers": {
          "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
          "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
          "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "ServiceUnavailable": {
        "description": "A required service is temporarily unavailable. Retry with exponential backoff.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "parameters": {
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Max results (1-50).",
        "schema": { "type": "integer", "minimum": 1, "maximum": 50 }
      },
      "Offset": {
        "name": "offset",
        "in": "query",
        "required": false,
        "description": "Number of results to skip for pagination (drawn from a bounded ranked pool).",
        "schema": { "type": "integer", "minimum": 0, "default": 0 }
      },
      "Platform": {
        "name": "platform",
        "in": "query",
        "required": false,
        "description": "Pin to a platform when a handle exists on both. Defaults to the highest-follower account.",
        "schema": { "type": "string", "enum": ["tiktok", "instagram"] }
      }
    }
  },
  "paths": {
    "/api/v1/search": {
      "get": {
        "summary": "Search creators",
        "description": "Search TikTok and Instagram creators using natural language. Supports niche, location, follower range, gender, and content-style queries. Returns the top relevance-ranked matches; page deeper with `offset`.",
        "operationId": "searchCreators",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Natural language search query (min 2 characters).",
            "schema": { "type": "string", "minLength": 2 },
            "examples": {
              "niche": { "value": "beauty creators" },
              "location": { "value": "fitness creators in LA" },
              "compound": { "value": "skincare creators in NYC under 50k followers" }
            }
          },
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/Offset" }
        ],
        "responses": {
          "200": {
            "description": "Search results",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/Creator" } },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": { "type": "integer", "description": "Results on this page (alias of pagination.count)." },
                        "query": { "type": "string" },
                        "interpreted_as": { "type": ["string", "null"] },
                        "response_time_ms": { "type": "integer" },
                        "pagination": { "$ref": "#/components/schemas/Pagination" }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/CapReached" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    },
    "/api/v1/creators/{handle}": {
      "get": {
        "summary": "Get creator profile",
        "description": "Full profile for a specific creator. The catalog is keyed on (handle, platform), so use `platform` to disambiguate a handle that exists on both networks.",
        "operationId": "getCreator",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Creator handle without the @ symbol.",
            "schema": { "type": "string" }
          },
          { "$ref": "#/components/parameters/Platform" }
        ],
        "responses": {
          "200": {
            "description": "Creator profile",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "data": { "$ref": "#/components/schemas/CreatorDetail" } }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/CapReached" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/v1/creators/{handle}/similar": {
      "get": {
        "summary": "Find similar creators",
        "description": "Creators with the same vibe, audience, and niche as a seed handle. Uses vector similarity when the seed has an embedding (meta.mode='embedding'), else niche + follower-tier matching (meta.mode='fallback').",
        "operationId": "findSimilarCreators",
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "description": "Seed creator handle without the @ symbol.",
            "schema": { "type": "string" }
          },
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/Offset" },
          { "$ref": "#/components/parameters/Platform" }
        ],
        "responses": {
          "200": {
            "description": "Similar creators",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/CreatorDetail" } },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "mode": { "type": "string", "enum": ["embedding", "fallback"] },
                        "seed_handle": { "type": "string" },
                        "total": { "type": "integer", "description": "Results on this page (alias of pagination.count)." },
                        "pagination": { "$ref": "#/components/schemas/Pagination" }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/CapReached" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/v1/niches": {
      "get": {
        "summary": "List niches",
        "description": "Every niche in the index with the active creator count per niche, sorted largest-first. Uncapped — does not count against your monthly cap and stays callable after the cap is hit. CDN-cached for 1 hour.",
        "operationId": "listNiches",
        "responses": {
          "200": {
            "description": "Niche taxonomy",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "total_niches": { "type": "integer" },
                        "niches": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "niche": { "type": "string" },
                              "count": { "type": "integer" }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    },
    "/api/v1/stats": {
      "get": {
        "summary": "Database statistics",
        "description": "Total active creator count, the platforms present in the index, and a freshness timestamp. Uncapped.",
        "operationId": "getStats",
        "responses": {
          "200": {
            "description": "Database stats",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "total_creators": { "type": "integer" },
                        "platforms": { "type": "array", "items": { "type": "string", "enum": ["tiktok", "instagram"] } },
                        "platform": { "type": "string", "description": "Legacy single-string field — the platform with the most creators. Prefer `platforms`." },
                        "generated_at": {
                          "type": "string",
                          "format": "date-time",
                          "description": "When this catalog snapshot was calculated. This is not a creator-level freshness timestamp."
                        },
                        "last_updated": {
                          "type": "string",
                          "format": "date-time",
                          "deprecated": true,
                          "description": "Backward-compatible alias of generated_at."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    },
    "/api/v1/usage": {
      "get": {
        "summary": "API usage and quota",
        "description": "Current-month call count, monthly limit, and remaining calls for the authenticated key. Use it to render a quota bar or short-circuit calls before a 402.",
        "operationId": "getUsage",
        "responses": {
          "200": {
            "description": "Usage stats",
            "headers": {
              "X-RateLimit-Limit": { "$ref": "#/components/headers/X-RateLimit-Limit" },
              "X-RateLimit-Remaining": { "$ref": "#/components/headers/X-RateLimit-Remaining" },
              "X-RateLimit-Reset": { "$ref": "#/components/headers/X-RateLimit-Reset" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "tier": { "type": "string", "example": "free" },
                        "monthly_usage": { "type": "integer", "example": 37 },
                        "monthly_limit": { "type": "integer", "example": 100 },
                        "remaining": {
                          "type": ["integer", "null"],
                          "description": "Calls remaining for a capped key; null for an unlimited administrative key.",
                          "example": 63
                        },
                        "period_start": { "type": "string", "format": "date-time" }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/CapReached" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    }
  }
}
