openapi: 3.0.3
info:
  title: Zolltarifnummern API V2
  description: |
    API for searching customs tariff numbers (Combined Nomenclature / CN codes)
    using semantic search with AI embeddings.
    
    This is the V2 API using vector embeddings (Voyage AI) for semantic similarity search.
  version: '2.1'
  contact:
    url: https://www.zolltarifnummern.de

servers:
  - url: https://www.zolltarifnummern.de/api/v2
    description: Production

paths:
  /cnSuggest:
    get:
      summary: Semantic search for customs tariff numbers
      description: |
        Search for customs tariff numbers using semantic similarity.
        - Numeric search: Prefix match on cn_char
        - Text search: AI-powered semantic search using vector embeddings
        
        **Example URL:**
        ```
        https://www.zolltarifnummern.de/api/v2/cnSuggest?term=fresh%20beef&year=2026&lang=en
        ```
      operationId: cnSuggestSemantic
      tags:
        - Semantic Search
      parameters:
        - name: term
          in: query
          required: true
          description: Search term (number or text)
          schema:
            type: string
            minLength: 1
            maxLength: 100
          examples:
            number:
              value: "02075110"
              summary: Search by tariff number
            text:
              value: "fresh beef"
              summary: Semantic text search
            semantic:
              value: "laptop computer"
              summary: Semantic search finds "portable data processing machines"
        - name: year
          in: query
          required: false
          description: Nomenclature year
          schema:
            type: integer
            minimum: 2020
            maximum: 2030
            default: 2026
          example: 2026
        - name: lang
          in: query
          required: false
          description: Language
          schema:
            type: string
            enum: [de, en, fr]
            default: de
          example: de
      responses:
        '200':
          description: Search results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CnSuggestResponse'
              examples:
                numeric:
                  summary: Numeric search
                  value:
                    query: "02075110"
                    year: "2026"
                    lang: "de"
                    version: "2.1"
                    total: 1
                    suggestions:
                      - code: "02075110"
                        score: 1.0
                        value: "<span><em>02075110</em></span><br>Geese, whole, fresh"
                        data: "https://www.zolltarifnummern.de/2026/02075110"
                semantic:
                  summary: Semantic search
                  value:
                    query: "fresh beef"
                    year: "2026"
                    lang: "en"
                    version: "2.1"
                    total: 10
                    suggestions:
                      - code: "02013000"
                        score: 0.8606
                        value: "<span>02013000</span><br>Meat of bovine animals, boneless, fresh or chilled"
                        data: "https://www.zolltarifnummern.de/2026/02013000"
                      - code: "0201"
                        score: 0.8574
                        value: "<span>0201</span><br>Meat of bovine animals, fresh or chilled"
                        data: "https://www.zolltarifnummern.de/2026/0201"
        '422':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'
        '429':
          description: Rate limit exceeded (5 requests per minute)
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Request limit per minute
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Remaining requests
            Retry-After:
              schema:
                type: integer
              description: Seconds until rate limit resets
        '503':
          description: Service unavailable (Embedding or Search service)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchError'

components:
  schemas:
    CnSuggestResponse:
      type: object
      required:
        - query
        - year
        - lang
        - version
        - total
        - suggestions
      properties:
        query:
          type: string
          description: Sanitized search term
          example: "fresh beef"
        year:
          type: string
          description: Year
          example: "2026"
        lang:
          type: string
          description: Language
          enum: [de, en, fr]
          example: "de"
        version:
          type: string
          description: API version
          example: "2.1"
        total:
          type: integer
          description: Number of results
          example: 10
        suggestions:
          type: array
          items:
            $ref: '#/components/schemas/CnSuggestion'
    
    CnSuggestion:
      type: object
      properties:
        code:
          type: string
          description: Customs tariff number (2-8 digits)
          example: "02013000"
        score:
          type: number
          format: float
          description: Semantic similarity score (0-1)
          example: 0.8606
        value:
          type: string
          description: Formatted display with HTML
          example: "<span>02013000</span><br>Meat of bovine animals, boneless, fresh or chilled"
        data:
          type: string
          format: uri
          description: URL to detail page
          example: "https://www.zolltarifnummern.de/2026/02013000"
    
    ValidationError:
      type: object
      properties:
        message:
          type: string
          example: "The given data was invalid."
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          example:
            term: ["The term field is required."]
    
    SearchError:
      type: object
      properties:
        query:
          type: string
        year:
          type: string
        lang:
          type: string
        version:
          type: string
          example: "2.1"
        total:
          type: integer
          example: 0
        suggestions:
          type: array
          items: {}
          example: []
        error:
          type: string
          enum:
            - "Embedding service unavailable"
            - "Search service unavailable"
          example: "Embedding service unavailable"

  securitySchemes:
    rateLimiting:
      type: apiKey
      in: header
      name: X-RateLimit-Limit
      description: |
        Rate limit: 5 requests per minute
        
        This endpoint uses AI embeddings which incur costs per request.

tags:
  - name: Semantic Search
    description: AI-powered semantic search for customs tariff numbers using vector embeddings
