OpenMercantil Risk Signals API

Documentary risk signals from public sources (AEPD, CNMC, concursos, AEAT moroso, CENDOJ)

OpenAPI Specification

openmercantil-risk-signals-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: OpenMercantil Risk Signals API
  version: 1.9.3
  summary: Versioned public-read, browser-account, billing, support and provider-callback contracts.
  description: 'Public JSON API for Spanish company information derived from BORME and other public sources.
    OpenMercantil is an independent informational service; it is NOT the BOE, BORME or Registro Mercantil
    and does NOT replace official certificates or registry extracts.


    **Rate limits.** Free: 60 req/min y 200 req/día por IP. Planes superiores (Profesional 5.000 req/día,
    MAX 50.000 req/día, Enterprise 500.000+ req/día) según cuenta y API key. Cabeceras `X-RateLimit-Limit`,
    `X-RateLimit-Remaining`, `X-RateLimit-Reset`, `X-OpenMercantil-Plan`, `Retry-After`.


    **License and attribution.** Source-specific metadata in each response and the active versioned source
    catalog prevails. OpenMercantil does not relicense upstream content under a blanket license. Unknown,
    review and restricted datasets are omitted or return `503 legal_layer_unavailable`. BOE/BORME material
    is re-used under Ley 37/2007 and its official version remains boe.es. Court judgments are not exposed;
    CENDOJ remains citation-index only under CGPJ Reglamento 3/2010.


    **Machine-readable catalog (DCAT-AP-ES):** https://openmercantil.es/catalog.rdf'
  termsOfService: https://openmercantil.es/terminos-de-uso
  contact:
    name: OpenMercantil
    url: https://openmercantil.es/soporte
    email: social@openmercantil.es
  license:
    name: Source-specific upstream terms; see response catalog metadata
    url: https://openmercantil.es/terminos-de-uso
  x-publisher:
    name: OpenMercantil
    url: https://openmercantil.es/
    email: social@openmercantil.es
  x-spatial: http://publications.europa.eu/resource/authority/country/ESP
  x-temporal: 2009-01-01/..
  x-language: es
  x-dcat-catalog: https://openmercantil.es/catalog.rdf
  x-rate-limit:
    free:
      per_min: 60
      per_day: 200
      kind: anonymous-ip
    profesional:
      per_min: 120
      per_day: 5000
      kind: api-key
    max:
      per_min: 600
      per_day: 50000
      kind: api-key
    enterprise:
      per_min: 1200
      per_day: 500000
      kind: contract
  x-methodology: https://openmercantil.es/metodologia
  x-sources: https://openmercantil.es/fuentes
  x-corrections: https://openmercantil.es/correcciones
  x-contract-status: Public read, browser-account and provider-callback surfaces are explicitly separated
    in this contract. Operator/admin routes are excluded. The public MCP consumes only the allowlisted
    GET read plane.
  x-account-segment-contract:
    projection: company_public_v2 immutable corporate sidecar
    synchronous_row_cap: 500
    bounded_count_cap: 50001
    count_semantics: The segment run response count is the number of rows returned, never a global total.
      Dataset preview uses total_is_lower_bound=true and total_lower_bound when the bounded count reaches
      50001.
    related_web_dataset_surface:
      preview_path: /mi-cuenta/datasets/preview
      export_path: /mi-cuenta/datasets/export.csv
      synchronous_export_max_rows: 500
      overflow_status: 503
      overflow_error: async_export_required
  x-company-identity-contract:
    version: '1.0'
    projection: company_public_v2 immutable generation-bound corporate sidecar
    applies_to: Every /api/v1/company/{slug}*, /api/v1/empresa/{slug}* and /api/v1/grafo/{slug} read before
      any report, cache, graph or dataset lookup. /api/v1/companies/compare resolves both requested subjects
      in one bounded company_public_v2 batch before either row is exposed; MCP company tools inherit these
      preflights through REST.
    resolution:
      published: canonical corporate slug admitted
      safe_alias: internally canonicalized and Content-Location emitted
      withheld: neutral 404; includes absent, personal and ambiguous/quarantined identities
      unavailable: 503 with no-store; clients must not infer absence
    search: Exact corporate CIF, exact canonical/safe-alias slug, or bounded name_prefix2 pool scored
      in application code. DNI/NIE and ambiguous CIFs return zero items.
    public_company_count: company_public_projection_state.row_count
servers:
- url: https://openmercantil.es
  description: Production
tags:
- name: Risk Signals
  description: Documentary risk signals from public sources (AEPD, CNMC, concursos, AEAT moroso, CENDOJ)
paths:
  /api/v1/company/{slug}/sanctions:
    get:
      operationId: getCompanyBySlugSanctions
      tags:
      - Companies
      - Risk Signals
      summary: Get policy-gated sanctions dataset
      description: This route is fail-closed. It returns data only when the exact sanctions dataset has
        a valid public decision, license metadata and identity policy in the active source catalog. OpenSanctions
        is currently in review, so the route returns 503 without reading or serializing its payload.
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Sanctions list when the exact dataset is authorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompanySanctionsResponse'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/LegalLayerUnavailable'
  /api/v1/company/{slug}/risk-signals:
    get:
      operationId: getCompanyBySlugRiskSignals
      tags:
      - Companies
      - Risk Signals
      summary: Get documentary risk signals
      description: Return only documentary signals whose exact dataset and negative-claim surface are
        authorized by the active source catalog. Other integrated sources are omitted without implying
        absence of the underlying fact. CENDOJ judgment text, OpenSanctions, AEPD and any unknown dataset
        are not exposed unless a future version records a field-specific public decision. Appearance never
        implies guilt, infringement, responsibility or current liability.
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Risk signals with disclaimers
          content:
            application/json:
              schema:
                type: object
                properties:
                  slug:
                    type: string
                  signals_count:
                    type: integer
                  signals:
                    type: array
                    items:
                      type: object
                      properties:
                        type:
                          type: string
                          enum:
                          - aepd_sanction
                          - cnmc_sanction
                          - concurso
                          - aeat_moroso
                          - cendoj_judgment
                          - opensanctions
                          - other
                        source_slug:
                          type: string
                        source_url:
                          type: string
                        date:
                          type: string
                          format: date
                        summary:
                          type: string
                        confidence:
                          type: string
                          enum:
                          - high
                          - medium
                          - low
                        homonymy_warning:
                          type: boolean
                        disclaimer:
                          type: string
                  source_catalog_version:
                    type: string
                  policy_scope:
                    type: string
                  _legal_notice:
                    type: string
                    example: Documentary signals only. Appearance does not imply guilt, infringement or
                      current liability.
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/PublicReadUnavailable'
  /api/v1/company/{slug}/aeat-moroso:
    get:
      operationId: getCompanyBySlugAeatMoroso
      tags:
      - Companies
      - Risk Signals
      summary: Check policy-gated AEAT debtor-list mention
      description: Negative-claim route gated before lookup. It returns 503 until the exact AEAT dataset
        has validated reuse, identity-match and cite-or-abstain policy metadata. Appearance would be documentary
        only and never imply current liability.
      x-rate-limit: free 60/min · 200/day
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Debtor list mention or empty
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompanyAeatDebtorResponse'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/LegalLayerUnavailable'
  /api/v1/company/{slug}/embargoes:
    get:
      operationId: getCompanyBySlugEmbargoes
      tags:
      - Companies
      - Risk Signals
      summary: Documentary embargo/garnishment mentions
      description: Return documentary mentions of embargoes (embargo de bienes, anotaciones registrales)
        when published in public registries. Documentary only — does not certify current status.
      x-rate-limit: free 60/min · 200/day
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Embargo mentions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompanyEmbargoesResponse'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/LegalLayerUnavailable'
components:
  headers:
    NoStoreCacheControl:
      description: Error responses must not be stored.
      schema:
        type: string
        const: no-store
  responses:
    LegalLayerUnavailable:
      description: 'Controlled fail-closed denial: the required dataset or legal layer is absent, invalid,
        unsupported or not authorized for this public surface.'
      headers:
        Cache-Control:
          $ref: '#/components/headers/NoStoreCacheControl'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: legal_layer_unavailable
            detail: Este dataset no esta habilitado para redistribucion publica por la politica de fuentes
              activa.
            source_catalog_version: 2026-07-12.2
    NotFound:
      description: Resource not found
      headers:
        Cache-Control:
          $ref: '#/components/headers/NoStoreCacheControl'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    PublicReadUnavailable:
      description: The public read failed closed because its legal source catalog, subject classification,
        bounded projection, database helper or required artifact is unavailable. Clients must not infer
        an empty result.
      headers:
        Cache-Control:
          description: Unavailable public reads are never cacheable.
          schema:
            type: string
            const: no-store
        Retry-After:
          description: Optional number of seconds to wait before retrying.
          schema:
            type: integer
            minimum: 1
      content:
        application/json:
          schema:
            anyOf:
            - $ref: '#/components/schemas/ProjectionUnavailableError'
            - $ref: '#/components/schemas/ErrorResponse'
          examples:
            legal_layer:
              value:
                error: legal_layer_unavailable
            company_identity:
              value:
                error: company_public_projection_unavailable
            projection:
              value:
                error: projection_unavailable
                detail: La proyección pública requerida no está disponible.
                projection: wikidata_company_v1
    TooManyRequests:
      description: Rate limit exceeded
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  schemas:
    CompanyAeatDebtorResponse:
      type: object
      required:
      - slug
      - listed
      properties:
        slug:
          type: string
        cif:
          type:
          - string
          - 'null'
        listed:
          type: boolean
        debt_eur:
          type:
          - number
          - 'null'
        period:
          type:
          - string
          - 'null'
        published_date:
          type:
          - string
          - 'null'
        history:
          type: array
          items:
            $ref: '#/components/schemas/JsonValue'
      additionalProperties:
        $ref: '#/components/schemas/JsonValue'
    CompanyEmbargoesResponse:
      type: object
      required:
      - slug
      - count
      - items
      - source
      - note
      properties:
        slug:
          type: string
        count:
          type: integer
        summary:
          anyOf:
          - type: object
            properties:
              count:
                type: integer
              total_amount_eur:
                type:
                - number
                - 'null'
              latest_date:
                type:
                - string
                - 'null'
            additionalProperties:
              $ref: '#/components/schemas/JsonValue'
          - type: 'null'
        items:
          type: array
          items:
            $ref: '#/components/schemas/EmbargoRecord'
          maxItems: 100
        source:
          type: string
        note:
          type: string
      additionalProperties: false
    CompanySanctionsResponse:
      type: object
      properties:
        slug:
          type: string
        count:
          type: integer
        items:
          type: array
          items:
            $ref: '#/components/schemas/DocumentarySanction'
        source:
          type:
          - string
          - 'null'
        note:
          type:
          - string
          - 'null'
      additionalProperties:
        $ref: '#/components/schemas/JsonValue'
    DocumentarySanction:
      type: object
      description: Sanitized documentary sanction record; appearance does not imply current liability.
      properties:
        source:
          type:
          - string
          - 'null'
        date:
          type:
          - string
          - 'null'
        authority:
          type:
          - string
          - 'null'
        summary:
          type:
          - string
          - 'null'
        official_url:
          type:
          - string
          - 'null'
          format: uri
        status:
          type:
          - string
          - 'null'
      additionalProperties:
        $ref: '#/components/schemas/JsonValue'
    EmbargoRecord:
      type: object
      description: Documentary official-notice record; may not reflect later unpublished resolutions.
      properties:
        date:
          type:
          - string
          - 'null'
        authority:
          type:
          - string
          - 'null'
        summary:
          type:
          - string
          - 'null'
        amount_eur:
          type:
          - number
          - 'null'
        source_url:
          type:
          - string
          - 'null'
          format: uri
      additionalProperties:
        $ref: '#/components/schemas/JsonValue'
    ErrorResponse:
      type: object
      description: Closed compatibility envelope for public/account errors. Route-specific schemas narrow
        these fields further where required.
      required:
      - error
      properties:
        error:
          type: string
          minLength: 1
        message:
          type: string
        detail:
          type: string
        code:
          type: string
        status:
          type:
          - integer
          - string
        projection:
          type: string
        reason:
          type: string
        source_catalog_version:
          type: string
        allowed_parameters:
          type: array
          uniqueItems: true
          items:
            type: string
        slug:
          type: string
        key:
          type: string
        maximum:
          type: integer
          minimum: 1
        parameter:
          type: string
        fields:
          type: array
          items:
            type: string
        max_bytes:
          type: integer
          minimum: 1
        allowed:
          type: array
          items:
            $ref: '#/components/schemas/JsonValue'
        valid:
          type: array
          items:
            $ref: '#/components/schemas/JsonValue'
        date:
          type: string
        login_url:
          type: string
        plan:
          type: string
        limited_by:
          type: string
          enum:
          - minute
          - day
        daily_limit:
          type: integer
          minimum: 1
        reset_at:
          type: integer
          minimum: 1
        reset_at_human:
          type: string
          format: date-time
        retry_after_s:
          type: integer
          minimum: 1
        retry_after:
          type: integer
          minimum: 1
        upgrade:
          type: string
          format: uri
        upgrade_url:
          type: string
        action:
          type: string
        limit:
          type: integer
          minimum: 0
        remaining:
          type: integer
          minimum: 0
        needed:
          type: integer
          minimum: 0
        shortfall:
          type: integer
          minimum: 0
        ok:
          type: boolean
        _alias_of:
          type: string
      additionalProperties: false
    JsonValue:
      description: A JSON value used only inside explicitly documented extension maps.
      oneOf:
      - type:
        - string
        - number
        - boolean
        - 'null'
      - type: array
        items:
          $ref: '#/components/schemas/JsonValue'
      - type: object
        additionalProperties:
          $ref: '#/components/schemas/JsonValue'
    ProjectionUnavailableError:
      type: object
      description: Fail-closed projection outage. Clients must not reinterpret this response as an empty
        or negative result.
      required:
      - error
      - detail
      - projection
      properties:
        error:
          type: string
          const: projection_unavailable
        detail:
          type: string
        projection:
          type: string
      additionalProperties: false