{"openapi":"3.0.3","info":{"title":"AISearchStackHub Partner API","description":"Authenticated endpoints for partner-scoped brand visibility data. The production rate limit is 60 requests per 60-second window per authenticated token, shared across all three partner GET operations; this is a fixed-window limit. The bucket is process-local and in-memory; it is not deployment-wide or IP-based; separate processes or instances can have separate buckets, and a process restart clears its bucket. Only requests passing authentication, and UUID validation for scoped visibility operations, reach the limiter. Requests beyond the allowance return 429 Too Many Requests with runtime-calculated Retry-After and error.retry_after values. No remaining-quota, absolute-reset, or X-RateLimit-* headers, separate reset timestamp, or reset endpoint are provided.","version":"1.0.0"},"servers":[{"url":"https://aisearchstackhub.ai","description":"AISearchStackHub"}],"security":[{"PartnerBearerAuth":[]}],"paths":{"/api/partner/brands":{"get":{"operationId":"listAccessibleBrands","summary":"List accessible brands","description":"Returns every brand explicitly scoped to the authenticated partner token in one response, ordered by brand_id. This endpoint has no supported pagination parameters or pagination metadata, so callers do not make follow-up page requests.","security":[{"PartnerBearerAuth":[]}],"responses":{"200":{"description":"Accessible brands, or an empty collection when the token has no brand scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandCollection"}}}},"401":{"description":"The Bearer token is missing, malformed, or invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"PartnerTokenRequired":{"$ref":"#/components/examples/PartnerTokenRequired"},"PartnerTokenInvalid":{"$ref":"#/components/examples/PartnerTokenInvalid"}}}}},"429":{"description":"The authenticated token exceeded the process-local 60-request/60-second partner API rate limit. Only requests passing authentication, and UUID validation for scoped visibility operations, reach this limiter. The response includes the runtime-calculated seconds until the current window expires in both the Retry-After header and error.retry_after; no remaining-quota, absolute-reset, or X-RateLimit-* headers, separate reset timestamp, or reset endpoint are provided.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"RateLimited":{"$ref":"#/components/examples/RateLimited"}}}},"headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"}}},"500":{"description":"The server could not list accessible brands.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"ListBrandsInternalError":{"$ref":"#/components/examples/ListBrandsInternalError"}}}}}}}},"/api/partner/brands/{brandId}/visibility":{"get":{"operationId":"getLatestVisibility","summary":"Get the latest visibility score","description":"Returns the latest completed visibility_weekly report for an authorized brand.","security":[{"PartnerBearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/BrandId"}],"responses":{"200":{"description":"Latest completed visibility report.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VisibilityReport"}}}},"400":{"description":"The brandId path parameter is not a UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"InvalidBrandId":{"$ref":"#/components/examples/InvalidBrandId"}}}}},"401":{"description":"The Bearer token is missing, malformed, or invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"PartnerTokenRequired":{"$ref":"#/components/examples/PartnerTokenRequired"},"PartnerTokenInvalid":{"$ref":"#/components/examples/PartnerTokenInvalid"}}}}},"403":{"description":"The partner token is not scoped to this brand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"BrandAccessDenied":{"$ref":"#/components/examples/BrandAccessDenied"}}}}},"404":{"description":"No completed weekly visibility report exists for this brand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"VisibilityNotFound":{"$ref":"#/components/examples/VisibilityNotFound"}}}}},"429":{"description":"The authenticated token exceeded the process-local 60-request/60-second partner API rate limit. Only requests passing authentication, and UUID validation for scoped visibility operations, reach this limiter. The response includes the runtime-calculated seconds until the current window expires in both the Retry-After header and error.retry_after; no remaining-quota, absolute-reset, or X-RateLimit-* headers, separate reset timestamp, or reset endpoint are provided.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"RateLimited":{"$ref":"#/components/examples/RateLimited"}}}},"headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"}}},"500":{"description":"The server could not fetch visibility data.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"VisibilityInternalError":{"$ref":"#/components/examples/VisibilityInternalError"}}}}}}}},"/api/partner/brands/{brandId}/visibility/history":{"get":{"operationId":"getVisibilityHistory","summary":"Get historical visibility scores","description":"Returns completed visibility_weekly reports newest first as a direct JSON array. Inclusive from and to filters bound the date range; limit caps the results at 12 by default and 100 maximum. The response has no page, offset, cursor, total-count, or next-page metadata. To retrieve longer histories, make bounded, non-overlapping date-window requests small enough to fit the limit and combine the returned arrays.","security":[{"PartnerBearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/BrandId"},{"name":"from","in":"query","required":false,"description":"Inclusive starting week. Must be a valid calendar date in YYYY-MM-DD format.","schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"to","in":"query","required":false,"description":"Inclusive ending week. Must be a valid calendar date in YYYY-MM-DD format.","schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of reports in this response, not a page size. Defaults to 12 and must be from 1 through 100; there is no page-based continuation mechanism.","schema":{"type":"integer","format":"int32","minimum":1,"maximum":100,"default":12}}],"responses":{"200":{"description":"Historical visibility reports, or an empty array when no reports match.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VisibilityReport"}}}}},"400":{"description":"A path or history filter failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"InvalidBrandId":{"$ref":"#/components/examples/InvalidBrandId"},"InvalidFromDate":{"$ref":"#/components/examples/InvalidFromDate"},"InvalidToDate":{"$ref":"#/components/examples/InvalidToDate"},"InvalidDateRange":{"$ref":"#/components/examples/InvalidDateRange"},"InvalidLimit":{"$ref":"#/components/examples/InvalidLimit"}}}}},"401":{"description":"The Bearer token is missing, malformed, or invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"PartnerTokenRequired":{"$ref":"#/components/examples/PartnerTokenRequired"},"PartnerTokenInvalid":{"$ref":"#/components/examples/PartnerTokenInvalid"}}}}},"403":{"description":"The partner token is not scoped to this brand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"BrandAccessDenied":{"$ref":"#/components/examples/BrandAccessDenied"}}}}},"429":{"description":"The authenticated token exceeded the process-local 60-request/60-second partner API rate limit. Only requests passing authentication, and UUID validation for scoped visibility operations, reach this limiter. The response includes the runtime-calculated seconds until the current window expires in both the Retry-After header and error.retry_after; no remaining-quota, absolute-reset, or X-RateLimit-* headers, separate reset timestamp, or reset endpoint are provided.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"RateLimited":{"$ref":"#/components/examples/RateLimited"}}}},"headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"}}},"500":{"description":"The server could not fetch visibility history.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerApiError"},"examples":{"HistoryInternalError":{"$ref":"#/components/examples/HistoryInternalError"}}}}}}}}},"components":{"securitySchemes":{"PartnerBearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"partner-token","description":"Active partner token sent as Authorization: Bearer <partner-token>."}},"parameters":{"BrandId":{"name":"brandId","in":"path","required":true,"description":"UUID of the authorized brand.","schema":{"type":"string","format":"uuid"}}},"headers":{"RetryAfter":{"description":"Runtime-calculated number of seconds until the current fixed window expires. This header is emitted on throttled responses; the value is not an absolute reset timestamp.","schema":{"type":"integer","minimum":1}}},"schemas":{"PartnerApiError":{"type":"object","description":"Ordinary failures use { \"error\": { \"code\": \"...\", \"message\": \"...\" } }. Only 429 responses include error.retry_after; the Retry-After header is also limited to 429 responses.","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"retry_after":{"type":"integer","minimum":1,"description":"Runtime-calculated number of seconds until the current fixed window expires; emitted on 429 rate-limited responses."}}}}},"BrandCollection":{"type":"object","required":["brands"],"properties":{"brands":{"type":"array","description":"Brands explicitly scoped to the partner token.","items":{"$ref":"#/components/schemas/Brand"}}}},"Brand":{"type":"object","required":["brand_id","name","monitoring_enabled","latest_visibility_at"],"properties":{"brand_id":{"type":"string","format":"uuid"},"name":{"type":"string","nullable":true},"monitoring_enabled":{"type":"boolean"},"latest_visibility_at":{"type":"string","format":"date-time","nullable":true}}},"VisibilityReport":{"type":"object","required":["brand_id","visibility_score","score_timestamp","score_week","engine_breakdown"],"properties":{"brand_id":{"type":"string","format":"uuid"},"visibility_score":{"type":"integer","minimum":0,"maximum":100},"score_timestamp":{"type":"string","format":"date-time"},"score_week":{"type":"string","format":"date"},"engine_breakdown":{"type":"object","description":"Visibility data keyed by engine name.","additionalProperties":{"$ref":"#/components/schemas/EngineBreakdown"}}}},"EngineBreakdown":{"type":"object","required":["visibility_pct","position_avg","share"],"properties":{"visibility_pct":{"type":"integer","minimum":0,"maximum":100},"position_avg":{"type":"integer","nullable":true},"position_delta":{"type":"integer","description":"Present only when current and prior position_avg values are integers. Positive values mean the average position number worsened; negative values mean it improved."},"share":{"type":"object","description":"Rounded competitor-domain share percentages keyed by domain.","additionalProperties":{"type":"number","minimum":0,"maximum":100}}}}},"examples":{"InvalidBrandId":{"value":{"error":{"code":"invalid_brand_id","message":"brandId must be a UUID"}}},"InvalidFromDate":{"value":{"error":{"code":"invalid_from_date","message":"from must be a valid YYYY-MM-DD date"}}},"InvalidToDate":{"value":{"error":{"code":"invalid_to_date","message":"to must be a valid YYYY-MM-DD date"}}},"InvalidDateRange":{"value":{"error":{"code":"invalid_date_range","message":"from must be on or before to"}}},"InvalidLimit":{"value":{"error":{"code":"invalid_limit","message":"limit must be an integer between 1 and 100"}}},"PartnerTokenRequired":{"value":{"error":{"code":"partner_token_required","message":"Authorization Bearer token required"}}},"PartnerTokenInvalid":{"value":{"error":{"code":"partner_token_invalid","message":"Partner token is invalid"}}},"BrandAccessDenied":{"value":{"error":{"code":"brand_access_denied","message":"Partner token cannot access this brand"}}},"VisibilityNotFound":{"value":{"error":{"code":"visibility_not_found","message":"No completed weekly visibility report found"}}},"RateLimited":{"summary":"Illustrative 429 response; retry_after varies at runtime.","description":"The value 12 is illustrative only. The actual Retry-After header and error.retry_after value are calculated at runtime from the seconds remaining in the current fixed window.","value":{"error":{"code":"rate_limited","message":"Too many partner API requests","retry_after":12}}},"ListBrandsInternalError":{"value":{"error":{"code":"internal_error","message":"Unable to list accessible brands"}}},"VisibilityInternalError":{"value":{"error":{"code":"internal_error","message":"Unable to fetch visibility data"}}},"HistoryInternalError":{"value":{"error":{"code":"internal_error","message":"Unable to fetch visibility history"}}}}}}