OpenMercantil Persons API
Documentary mentions of natural persons in BORME (officer roles). Persons treated as documentary mentions only — no DNI, no contact data, no scoring.
Documentary mentions of natural persons in BORME (officer roles). Persons treated as documentary mentions only — no DNI, no contact data, no scoring.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/openmercantil-persons-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
openapi: 3.2.0
info:
title: Openmercantil Persons API
contact:
name: OpenMercantil
url: https://openmercantil.es/soporte
email: social@openmercantil.es
termsOfService: https://openmercantil.es/terminos-de-uso
x-refined-note:
- x-account-segment-contract differs across the merged source definitions and was not carried
- x-company-identity-contract differs across the merged source definitions and was not carried
- x-contract-status differs across the merged source definitions and was not carried
- x-corrections differs across the merged source definitions and was not carried
- x-dcat-catalog differs across the merged source definitions and was not carried
- x-language differs across the merged source definitions and was not carried
- x-methodology differs across the merged source definitions and was not carried
- x-publisher differs across the merged source definitions and was not carried
- x-rate-limit differs across the merged source definitions and was not carried
- x-sources differs across the merged source definitions and was not carried
- x-spatial differs across the merged source definitions and was not carried
- x-temporal differs across the merged source definitions and was not carried
version: '1.0'
description: 'Operations tagged Persons across 2 of this provider''s published API definitions: openmercantil-openapi-1.9.3.json, openmercantil-openapi.yml. Each path carries the servers of the definition it was published in.'
servers:
- url: https://openmercantil.es
description: Production
tags:
- name: Persons
description: Documentary mentions of natural persons in BORME (officer roles). Persons treated as documentary mentions only — no DNI, no contact data, no scoring.
paths:
/api/v1/company/{slug}/officers:
get:
security:
- {}
- apiKey: []
- bearerAuth: []
x-api-credential-scope: companies:read
operationId: getCompanyBySlugOfficers
tags:
- Persons
summary: Get current and historical company officers
description: Return at most 500 officer mentions from the same cached, policy-filtered company projection used by the canonical company route. The JSON body is capped at 1 MiB. Persons are documentary mentions only — no DNI, contact data or personal address.
x-rate-limit: plan policy (see info.x-rate-limit)
parameters:
- name: slug
in: path
required: true
schema:
type: string
example: banco-santander-s-a
responses:
'200':
headers:
X-Data-Sources:
$ref: '#/components/headers/XDataSources'
X-Source-Catalog-Version:
$ref: '#/components/headers/XSourceCatalogVersion'
X-Attribution-Required:
$ref: '#/components/headers/XAttributionRequired'
description: Officer list
content:
application/json:
schema:
$ref: '#/components/schemas/OfficerList'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
'503':
description: Bounded cached officer projection unavailable
content:
application/json:
schema:
$ref: '#/components/schemas/OfflineProjectionError'
servers:
- url: https://openmercantil.es
description: Production
/api/v1/persona/{slug}:
get:
security:
- {}
- apiKey: []
- bearerAuth: []
x-api-credential-scope: people:read
operationId: getPersonaBySlug
tags:
- Persons
summary: Get documentary mentions of a person
description: Return an exact, attested person_public_v1 documentary-mention report bound to the same company_public_v2 source generation. Available is 200/private no-store; absent, withheld, ambiguous or quarantined is a neutral 404; authority, hash, binding or generation failure is 503/no-store. No raw persons fallback. No DNI/NIE, contact/address, photo/social data, enrichment, sanctions, scoring, person-procurement inference, identity resolution or vigency inference.
parameters:
- name: slug
in: path
required: true
schema:
type: string
example: calero-brazalez-juan-jose
responses:
'200':
headers:
X-Data-Sources:
$ref: '#/components/headers/XDataSources'
X-Source-Catalog-Version:
$ref: '#/components/headers/XSourceCatalogVersion'
X-Attribution-Required:
$ref: '#/components/headers/XAttributionRequired'
Cache-Control:
schema:
type: string
description: no-store
description: Person documentary mentions
content:
application/json:
schema:
$ref: '#/components/schemas/PersonDocumentaryReport'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
'503':
$ref: '#/components/responses/PublicReadUnavailable'
servers:
- url: https://openmercantil.es
description: Production
/api/v1/person/{slug}:
get:
operationId: getLegacyPersonBySlug
tags:
- Persons
summary: Get documentary person mentions through the legacy English alias
description: Deprecated alias of `GET /api/v1/persona/{slug}`. The response remains a neutral set of attributed BORME documentary mentions, never an identity profile.
deprecated: true
x-replaced-by: /api/v1/persona/{slug}
parameters:
- name: slug
in: path
required: true
schema:
type: string
example: calero-brazalez-juan-jose
responses:
'200':
description: Same closed person_public_v1 report as the canonical Spanish route
headers:
Content-Location:
schema:
type: string
description: Canonical /api/v1/persona/{slug} path
X-Data-Sources:
$ref: '#/components/headers/XDataSources'
X-Source-Catalog-Version:
$ref: '#/components/headers/XSourceCatalogVersion'
X-Attribution-Required:
$ref: '#/components/headers/XAttributionRequired'
Cache-Control:
schema:
type: string
const: private, no-store
content:
application/json:
schema:
$ref: '#/components/schemas/PersonDocumentaryReport'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
'503':
$ref: '#/components/responses/PublicReadUnavailable'
servers:
- url: https://openmercantil.es
description: Production
/api/v1/person/search:
get:
security:
- {}
- apiKey: []
- bearerAuth: []
x-api-credential-scope: people:read
operationId: getPersonSearch
x-query-contract:
allowed:
- q
- limit
unknown: 400 invalid_parameter
non_scalar: 400 invalid_parameter
lexical: 400 invalid_parameter
range: 422 validation_failed
tags:
- Persons
summary: Search documentary mentions of persons
description: Search only person_public_search_v1, bounded to 50 results and revalidated against the activated company_public_v2 generation. Results are unresolved name mentions, never identity profiles. Authority failure is 503/no-store, not an empty result. No raw persons, enrichment or person-procurement fallback.
x-rate-limit: free 60/min · 200/day
parameters:
- name: q
in: query
required: true
schema:
type: string
minLength: 2
maxLength: 200
example: garcia
- name: limit
in: query
required: false
schema:
type: integer
minimum: 1
maximum: 50
default: 20
responses:
'200':
headers:
X-Data-Sources:
$ref: '#/components/headers/XDataSources'
X-Source-Catalog-Version:
$ref: '#/components/headers/XSourceCatalogVersion'
X-Attribution-Required:
$ref: '#/components/headers/XAttributionRequired'
Cache-Control:
schema:
type: string
const: private, no-store
description: Person search results
content:
application/json:
schema:
$ref: '#/components/schemas/PersonSearchResponse'
'400':
$ref: '#/components/responses/BadRequest'
'422':
$ref: '#/components/responses/ValidationFailed'
'429':
$ref: '#/components/responses/TooManyRequests'
'503':
$ref: '#/components/responses/PublicReadUnavailable'
servers:
- url: https://openmercantil.es
description: Production
/api/v1/persona/{slug}/contracts:
get:
operationId: getPersonaBySlugContracts
tags:
- Persons
summary: Person-to-procurement derivation (unavailable)
description: Fail-closed derived route. It always returns 503 before reading data because `person_contracts_v1` is not authorized. A future implementation requires a new reviewed projection and contract version.
deprecated: true
x-rate-limit: plan policy (see info.x-rate-limit)
parameters:
- name: slug
in: path
required: true
schema:
type: string
responses:
'429':
$ref: '#/components/responses/TooManyRequests'
'503':
$ref: '#/components/responses/LegalLayerUnavailable'
servers:
- url: https://openmercantil.es
description: Production
components:
responses:
ValidationFailed:
description: The query is lexically valid but outside a documented numeric or length bound, or uses an unsupported indexed combination.
headers:
Cache-Control:
$ref: '#/components/headers/NoStoreCacheControl'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
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
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'
BadRequest:
description: Invalid request
headers:
Cache-Control:
$ref: '#/components/headers/NoStoreCacheControl'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
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
BadRequest_2:
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse_2'
TooManyRequests_2:
description: Rate limit exceeded
headers:
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
X-RateLimit-Limit:
description: Maximum requests per window (60 per minute by IP on the public anonymous tier).
schema:
type: integer
X-RateLimit-Remaining:
description: Remaining requests in the current window.
schema:
type: integer
X-RateLimit-Reset:
description: Unix timestamp when the rate-limit counter resets.
schema:
type: integer
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse_2'
NotFound_2:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse_2'
schemas:
PersonDocumentaryReport:
type: object
description: Exact person_public_v1 report. It groups unresolved BORME name mentions and never proves identity or current vigency.
required:
- slug
- name
- first_seen
- last_seen
- companies_count
- active_positions
- inactive_positions
- subject_type
- identity_resolution
- positions_semantics
- _legal_notice
- _correction_channel
- schema_version
- status
- projection
- _source_catalog
- _data_sources_used
properties:
slug:
type: string
pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$
name:
type: string
minLength: 1
first_seen:
type:
- string
- 'null'
format: date
last_seen:
type:
- string
- 'null'
format: date
companies_count:
type: integer
minimum: 1
active_positions:
type: array
items:
$ref: '#/components/schemas/PersonDocumentaryPosition'
inactive_positions:
type: array
items:
$ref: '#/components/schemas/PersonDocumentaryPosition'
subject_type:
type: string
const: person_documentary_mentions
identity_resolution:
type: string
const: not_performed
positions_semantics:
type: string
minLength: 1
_legal_notice:
type: string
minLength: 1
_correction_channel:
type: string
format: uri
const: https://openmercantil.es/proteccion-de-datos/derechos
schema_version:
type: string
const: person_documentary_report_v1
status:
type: string
const: available
projection:
$ref: '#/components/schemas/PersonPublicProjectionMetadata'
_source_catalog:
$ref: '#/components/schemas/SourceCatalogEnvelope'
_data_sources_used:
type: array
minItems: 1
items:
$ref: '#/components/schemas/PublicSourcePolicyMetadata'
_attributions:
type: object
additionalProperties:
type: string
additionalProperties: false
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
OfficerDocumentaryMention:
type: object
description: Documentary officer mention. It never contains DNI, contact details or a personal address.
properties:
name:
type: string
person_slug:
type:
- string
- 'null'
role:
type:
- string
- 'null'
appointed_at:
type:
- string
- 'null'
ended_at:
type:
- string
- 'null'
source:
type:
- string
- 'null'
source_url:
type:
- string
- 'null'
format: uri
additionalProperties:
$ref: '#/components/schemas/JsonValue'
PersonPublicProjectionMetadata:
type: object
required:
- name
- schema_version
- contract_sha256
- source_generation
- content_sha256
- projected_at
properties:
name:
type: string
const: person_public_v1
schema_version:
type: string
const: '1.0'
contract_sha256:
type: string
pattern: ^[a-f0-9]{64}$
source_generation:
type: string
pattern: ^cpv2-[a-f0-9]{64}$
content_sha256:
type: string
pattern: ^[a-f0-9]{64}$
projected_at:
type: string
format: date-time
additionalProperties: false
SourceCatalogEnvelope:
type: object
required:
- catalog_version
- policy_fingerprint
- sources
properties:
catalog_version:
type: string
policy_fingerprint:
type: string
policy_effective_date:
type: string
format: date
sources:
type: object
additionalProperties:
$ref: '#/components/schemas/PublicSourcePolicyMetadata'
additionalProperties: false
PersonSearchItem:
type: object
description: Documentary person search result; no DNI, contact data or personal address.
required:
- slug
- name
- mentions_count
- companies_count
- first_seen
- last_seen
- subject_type
- identity_resolution
- source_slug
properties:
slug:
type: string
pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$
name:
type: string
minLength: 1
mentions_count:
type: integer
minimum: 1
companies_count:
type: integer
minimum: 1
first_seen:
type:
- string
- 'null'
format: date
last_seen:
type:
- string
- 'null'
format: date
subject_type:
type: string
const: person_documentary_mentions
identity_resolution:
type: string
const: not_performed
source_slug:
type: string
const: borme
additionalProperties: false
OfflineProjectionError:
type: object
required:
- error
properties:
error:
type: string
projection:
type:
- string
- 'null'
derivation:
type:
- string
- 'null'
detail:
type:
- string
- 'null'
additionalProperties: false
PersonDocumentaryPosition:
type: object
required:
- company
- company_name
- company_slug
- cif
- role
- since
- until
- documentary_status
- vigency_verified
- source_slug
- source_url
properties:
company:
type: string
minLength: 1
company_name:
type: string
minLength: 1
company_slug:
type: string
pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$
cif:
type: string
description: Admitted legal-entity CIF; never a natural-person identifier.
role:
type:
- string
- 'null'
since:
type:
- string
- 'null'
format: date
until:
type:
- string
- 'null'
format: date
documentary_status:
type: string
enum:
- open_documentary_mention
- historical_documentary_mention
vigency_verified:
type: boolean
const: false
source_slug:
type: string
const: borme
source_url:
type: string
format: uri
const: https://www.boe.es/diario_borme/
additionalProperties: false
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
OfficerList:
type: array
maxItems: 500
items:
$ref: '#/components/schemas/OfficerDocumentaryMention'
PersonSearchResponse:
type: object
required:
- schema_version
- status
- query
- count
- items
- subject_type
- identity_resolution
- projection
- _legal_notice
- _source_catalog
- _data_sources_used
properties:
schema_version:
type: string
const: person_search_v1
status:
type: string
const: available
query:
type: string
minLength: 2
maxLength: 200
count:
type: integer
minimum: 0
maximum: 50
items:
type: array
maxItems: 50
items:
$ref: '#/components/schemas/PersonSearchItem'
subject_type:
type: string
const: person_documentary_mentions
identity_resolution:
type: string
const: not_performed
projection:
$ref: '#/components/schemas/PersonPublicProjectionMetadata'
_legal_notice:
type: string
minLength: 1
_source_catalog:
$ref: '#/components/schemas/SourceCatalogEnvelope'
_data_sources_used:
type: array
minItems: 1
items:
$ref: '#/components/schemas/PublicSourcePolicyMetadata'
_attributions:
type: object
additionalProperties:
type: string
additionalProperties: false
PublicSourcePolicyMetadata:
type: object
additionalProperties: false
required:
- slug
- name
- license
- attribution_required
- reuse_conditions
- policy_effective_date
- reviewed_at
- catalog_version
properties:
slug:
type: string
name:
type: string
license:
type: string
license_url:
type:
- string
- 'null'
format: uri
attribution_required:
type: boolean
attribution_text:
type:
- string
- 'null'
official_url:
type:
- string
- 'null'
format: uri
reuse_conditions:
type: string
description: Condiciones de reutilizacion que el consumidor debe conservar al presentar o transformar el dato.
policy_effective_date:
type: string
format: date
reviewed_at:
type: string
format: date
data_updated_at:
type:
- string
- 'null'
format: date-time
catalog_version:
type: string
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'
Officer:
type: object
properties:
person_slug:
type: string
name:
type: string
role:
type: string
appointed:
type:
- string
- 'null'
format: date
ceased:
type:
- string
- 'null'
format: date
additionalProperties: true
PersonSearchItem_2:
type: object
properties:
slug:
type: string
name:
type: string
company_count:
type: integer
additionalProperties: true
PersonReport:
type: object
properties:
slug:
type: string
name:
type: string
roles:
type: array
items:
$ref: '#/components/schemas/Officer'
additionalProperties: true
ErrorResponse_2:
type: object
properties:
error:
type: string
message:
type: string
additionalProperties: true
PersonSearchResponse_2:
type: object
properties:
query:
type: string
count:
type: integer
items:
type: array
items:
$ref: '#/components/schemas/PersonSearchItem_2'
additionalProperties: true
headers:
XSourceCatalogVersion:
description: Mandatory on every successful public GET. Exact version of the legal source catalog used to authorize the response.
schema:
type: string
minLength: 1
example: 2026-07-12.2
NoStoreCacheControl:
description: Error responses must not be stored.
schema:
type: string
const: no-store
XAttributionRequired:
description: Optional. When present, comma-separated public source aliases whose attribution terms must accompany reuse.
schema:
type: string
minLength: 1
pattern: ^[a-z0-9][a-z0-9_.-]*(,[a-z0-9][a-z0-9_.-]*)*$
example: placsp
XDataSources:
description: Comma-separated aliases from the active public source catalog that contributed to the response. Omitted only when a valid exact filter returns an empty representation with zero contributing sources.
schema:
type: string
minLength: 1
pattern: ^[a-z0-9][a-z0-9_.-]*(,[a-z0-9][a-z0-9_.-]*)*$
example: borme,placsp
securitySchemes:
cookieAuth:
type: apiKey
in: cookie
name: ob_sess
description: Browser session cookie set after login at /mi-cuenta/login. Mutations also require X-CSRF-Token header (obtain via GET /api/v1/user/me).
apiKey:
type: apiKey
in: header
name: X-API-Key
description: Optional opaque omk_* API credential for public GETs. Anonymous access remains valid; a credential with the operation's x-api-credential-scope (or public:read) selects its account quota. Never place credentials in query strings.
bearerAuth:
type: http
scheme: bearer
bearerFormat: opaque omk_* credential
description: 'Optional Authorization: Bearer transport for the same opaque omk_* API credential accepted by X-API-Key. It is not a JWT or OAuth access token.'
sessionCookie:
type: apiKey
in: cookie
name: session
description: Session cookie issued after web sign-in, required only for billing endpoints.
externalDocs:
description: Documentación narrativa con ejemplos en curl/Python/JavaScript
url: https://openmercantil.es/api/documentacion
x-refined-from:
- openmercantil-openapi-1.9.3.json
- openmercantil-openapi.yml