openapi: 3.1.0
info:
  title: Comparr API
  version: "1.0.0"
  summary: French AI product comparisons for agents
  description: |
    Comparr exposes product comparisons, AI rankings (best / value / budget),
    live merchant prices and versus pages. Designed for AI agents (ChatGPT, Grok,
    Gemini, Claude, Perplexity…).

    Prefer existing comparatifs via GET endpoints. POST /searches creates a new
    pipeline run and is globally limited per day; cached queries do not consume
    the search quota. Poll GET /searches/{id} until status is completed.
  contact:
    name: Comparr
    url: https://www.comparr.fr/api/docs
servers:
  - url: https://www.comparr.fr/api/v1
    description: Comparr production/API base
tags:
  - name: discovery
  - name: comparatifs
  - name: products
  - name: versus
  - name: searches
paths:
  /meta:
    get:
      operationId: getMeta
      tags: [discovery]
      summary: API metadata and remaining quotas
      responses:
        "200":
          description: Metadata
  /comparatifs:
    get:
      operationId: listComparatifs
      tags: [comparatifs]
      summary: List indexable product comparisons
      parameters:
        - name: page
          in: query
          schema: { type: integer, minimum: 1, default: 1 }
        - name: per_page
          in: query
          schema: { type: integer, minimum: 1, maximum: 50, default: 20 }
      responses:
        "200":
          description: Paginated list
        "429":
          description: Daily read quota exceeded
  /comparatifs/{id}:
    get:
      operationId: getComparatif
      tags: [comparatifs]
      summary: Comparison detail (verdict, rankings, FAQ, products, offers)
      parameters:
        - $ref: "#/components/parameters/IdOrSlug"
      responses:
        "200":
          description: Comparison payload
        "404":
          description: Not found
  /products/{id}:
    get:
      operationId: getProduct
      tags: [products]
      summary: Product sheet with merchant offers
      parameters:
        - $ref: "#/components/parameters/IdOrSlug"
      responses:
        "200":
          description: Product payload
        "404":
          description: Not found
  /versus:
    get:
      operationId: listVersus
      tags: [versus]
      summary: List completed X vs Y pages
      parameters:
        - name: page
          in: query
          schema: { type: integer, default: 1 }
        - name: per_page
          in: query
          schema: { type: integer, default: 20, maximum: 50 }
      responses:
        "200":
          description: Paginated versus list
  /versus/{id}:
    get:
      operationId: getVersus
      tags: [versus]
      summary: Versus page detail
      parameters:
        - $ref: "#/components/parameters/IdOrSlug"
      responses:
        "200":
          description: Versus payload
  /searches:
    post:
      operationId: createSearch
      tags: [searches]
      summary: Create or resolve a comparison by query
      description: |
        Returns 200 with full payload on cache hit / completed search.
        Returns 202 with poll_url when a new pipeline run is started.
        x-openai-isConsequential: true
      x-openai-isConsequential: true
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [query]
              properties:
                query:
                  type: string
                  example: robot piscine
      responses:
        "200":
          description: Cached / completed comparison
        "202":
          description: Search accepted — poll poll_url
        "429":
          description: Daily search or read quota exceeded
  /searches/{id}:
    get:
      operationId: getSearch
      tags: [searches]
      summary: Poll search status / get comparison
      parameters:
        - $ref: "#/components/parameters/IdOrSlug"
      responses:
        "200":
          description: Search status or full comparison when completed
components:
  parameters:
    IdOrSlug:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: Numeric id or FriendlyId slug
