openapi: 3.1.0
info:
  title: OpenSOSData API
  version: 1.2.0
  description: |
    Real-time US Secretary of State business entity and UCC filing lookups.
    Priced at $0.0314 per lookup (Pi). No subscription required.
    No minimum spend. Public government data should not cost more
    than a mathematical constant.
  contact:
    name: OpenSOSData
    url: https://opensosdata.com
servers:
  - url: https://api.opensosdata.com
    description: Production
security:
  - ApiKeyAuth: []
paths:
  /v1/lookup:
    post:
      summary: Look up a business entity or UCC filing by name and state
      operationId: lookupEntity
      x-payment-info:
        amount: "0.0314"
        currency: "usd"
        description: "Business entity or UCC filing lookup"
        methods:
          - type: "tempo"
            intent: "charge"
      security:
        - ApiKeyAuth: []
        - {}
      parameters:
        - name: fresh
          in: query
          required: false
          schema:
            type: boolean
          description: Legacy parameter — all lookups now return fresh data by default.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LookupRequest'
            examples:
              business_entity:
                summary: Business entity search
                value:
                  entity_name: Apple Inc
                  state: DE
              ucc:
                summary: UCC filing search
                value:
                  entity_name: Subway
                  state: FL
                  searchType: ucc
      responses:
        '200':
          description: Entity or UCC filing found
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/LookupSuccess'
                  - $ref: '#/components/schemas/UCCLookupSuccess'
        '202':
          description: Lookup queued for async processing (slow states)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AsyncJobCreated'
        '404':
          description: Entity not found (not billed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LookupError'
        '401':
          description: Missing or invalid API key
        '400':
          description: Invalid request body
        '402':
          description: |
            Payment required. The WWW-Authenticate header contains an MPP
            challenge. Present valid payment credentials to access this
            endpoint without an API key.
          headers:
            WWW-Authenticate:
              schema:
                type: string
              description: MPP payment challenge
          content:
            application/problem+json:
              schema:
                type: object
                properties:
                  type: { type: string, example: "https://paymentauth.org/problems/payment-required" }
                  title: { type: string, example: "Payment Required" }
                  status: { type: integer, example: 402 }
                  detail: { type: string }
                  challengeId: { type: string }
  /v1/lookup/status/{jobId}:
    get:
      summary: Poll async lookup job status
      operationId: lookupStatus
      security:
        - ApiKeyAuth: []
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
          description: Job ID returned in the 202 response
      responses:
        '200':
          description: Job status (pending, processing, or complete)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobStatus'
        '404':
          description: Job not found
        '500':
          description: Job failed
  /v1/account/balance:
    get:
      summary: Check account balance and remaining lookups
      operationId: accountBalance
      security:
        - ApiKeyAuth: []
      responses:
        '200':
          description: Account balance details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountBalance'
        '404':
          description: User not found
  /v1/states:
    get:
      summary: List all 50 states with their scraper status
      operationId: listStates
      security: []
      responses:
        '200':
          description: List of states
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/StateInfo'
  /v1/health:
    get:
      summary: Service health check
      operationId: health
      security: []
      responses:
        '200':
          description: Health status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Health'
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
  schemas:
    LookupRequest:
      type: object
      required: [entity_name, state]
      properties:
        entity_name:
          type: string
          minLength: 2
          maxLength: 200
          description: Business name or debtor name to search
        state:
          type: string
          minLength: 2
          maxLength: 2
          description: Two-letter US state code
        searchType:
          type: string
          enum: [business_entity, ucc]
          default: business_entity
          description: Type of search to perform
        searchBy:
          type: string
          enum: [debtor, secured_party]
          description: "Set to secured_party to search by secured party name instead of debtor. Supported states: AL, CA, CO, CT, FL, ID, KY, MA, MT, NC, OH, RI, WV"
        dateAfter:
          type: string
          format: date
          description: "Filter to filings filed after this date (YYYY-MM-DD). Supported states: CO, CA, ID, MT"
        dateBefore:
          type: string
          format: date
          description: "Filter to filings filed before this date (YYYY-MM-DD). Supported states: CO, CA, ID, MT"
    EntityData:
      type: object
      properties:
        entityName: { type: string }
        entityType: { type: string }
        entityId: { type: string }
        status: { type: string }
        formationDate: { type: string }
        registeredAgentName: { type: string }
        registeredAgentAddress: { type: string }
        registeredAgentCity: { type: string }
        registeredAgentState: { type: string }
        registeredAgentZip: { type: string }
        principalAddress: { type: string }
        principalCity: { type: string }
        principalState: { type: string }
        principalZip: { type: string }
        officers:
          type: array
          items:
            type: object
            properties:
              name: { type: string }
              title: { type: string }
        sosUrl: { type: string }
        scrapedAt: { type: string, format: date-time }
    LookupSuccess:
      type: object
      properties:
        success: { type: boolean, example: true }
        data: { $ref: '#/components/schemas/EntityData' }
        cost: { type: number, example: 0.0314 }
        state_scraper_version: { type: string, example: '3.0' }
        scraper_built_at: { type: string, format: date-time }
    UCCFiling:
      type: object
      properties:
        fileNumber: { type: string }
        fileDate: { type: string }
        debtorName: { type: string }
        securedParty: { type: string }
        collateral: { type: string }
        status: { type: string }
        lapseDate: { type: string }
    UCCLookupSuccess:
      type: object
      properties:
        success: { type: boolean, example: true }
        data:
          type: object
          properties:
            filings:
              type: array
              items:
                $ref: '#/components/schemas/UCCFiling'
            totalResults: { type: integer }
        cost: { type: number, example: 0.0314 }
    AsyncJobCreated:
      type: object
      properties:
        success: { type: boolean, example: true }
        async: { type: boolean, example: true }
        jobId: { type: string }
        message: { type: string }
        pollInterval: { type: integer, example: 3000, description: Recommended poll interval in milliseconds }
        estimatedSeconds: { type: integer, example: 60 }
    JobStatus:
      type: object
      properties:
        success: { type: boolean }
        status: { type: string, enum: [pending, processing, complete, failed] }
        message: { type: string }
        result:
          description: Present when status is complete — same shape as a 200 lookup response
          $ref: '#/components/schemas/LookupSuccess'
        pollInterval: { type: integer, example: 3000 }
        cost: { type: number, example: 0.0314 }
        error: { type: string, description: Present when status is failed }
    AccountBalance:
      type: object
      properties:
        success: { type: boolean, example: true }
        type: { type: string, enum: [user, internal] }
        balanceStored: { type: integer, description: Balance in stored units }
        balanceDisplay: { type: string, example: '.14' }
        lookupsRemaining: { type: integer }
        pricePerLookup: { type: number, example: 0.0314 }
        topUpUrl: { type: string }
    LookupError:
      type: object
      properties:
        success: { type: boolean, example: false }
        error: { type: string, example: Entity not found }
        cost: { type: number, example: 0, description: No charge on failed lookups }
    StateInfo:
      type: object
      properties:
        state: { type: string }
        status: { type: string, enum: [active, degraded, inactive] }
        version: { type: string }
        lastTestedAt: { type: string, format: date-time, nullable: true }
        available: { type: boolean }
        uccAvailable: { type: boolean }
    Health:
      type: object
      properties:
        status: { type: string, enum: [healthy, degraded] }
        checks:
          type: object
          properties:
            redis: { type: string }
            postgres: { type: string }
            degraded_scrapers: { type: integer }
            lookups_today: { type: integer }
            cache_hit_rate_pct: { type: number }
        timestamp: { type: string, format: date-time }
