Resolve the published exchange rate that applies on a given date
Returns the rate to apply to an operation on the requested date, following the rule in D.S. 29-94-EF artículo 5 numeral 17: the rate published on that date, or, on a day with no publication, EL ÚLTIMO PUBLICADO — the last one published on or before it. It never resolves forward and never interpolates. THE DATE IS A REQUIRED PATH SEGMENT WITH NO DEFAULT, on purpose: this service runs on UTC and Peru is UTC-05:00, so a server-side 'today' would resolve to tomorrow's date for every request sent after 19:00 in Lima, and the último-publicado rule would then hand back a rate labelled with a date the caller never asked about. The caller knows its comprobante's issue date; this service does not. BECAUSE THE ANSWER MAY COME FROM ANOTHER DAY, read rateDate, fallback and stalenessDays before displaying anything: fallback=true means rateDate differs from requestedOn, and stalenessDays is how far back the answer was taken from — 2 over a weekend is normal and correct under the rule, while 40 is not a market fact but a loader that stopped running. No ceiling is imposed here, because the norm has none; reporting is this API's half of the job and deciding is the caller's. Both sides are returned: for IGV the answer is sellRate ('promedio ponderado venta', for sales and purchases alike), while buyRate exists because the Impuesto a la Renta rule splits compra/venta by activo/pasivo. Rates are STRINGS with six decimals and must be parsed as decimals — routing a rate that gets multiplied into money through a binary float is the one place a fiscal figure can change value. Informational, like the padrón route: nothing here validates or blocks a document. Requires consultations:read.
Returns the rate to apply to an operation on the requested date, following the rule in D.S. 29-94-EF artículo 5 numeral 17: the rate published on that date, or, on a day with no publication, EL ÚLTIMO PUBLICADO — the last one published on or before it. It never resolves forward and never interpolates. THE DATE IS A REQUIRED PATH SEGMENT WITH NO DEFAULT, on purpose: this service runs on UTC and Peru is UTC-05:00, so a server-side 'today' would resolve to tomorrow's date for every request sent after 19:00 in Lima, and the último-publicado rule would then hand back a rate labelled with a date the caller never asked about. The caller knows its comprobante's issue date; this service does not. BECAUSE THE ANSWER MAY COME FROM ANOTHER DAY, read rateDate, fallback and stalenessDays before displaying anything: fallback=true means rateDate differs from requestedOn, and stalenessDays is how far back the answer was taken from — 2 over a weekend is normal and correct under the rule, while 40 is not a market fact but a loader that stopped running. No ceiling is imposed here, because the norm has none; reporting is this API's half of the job and deciding is the caller's. Both sides are returned: for IGV the answer is sellRate ('promedio ponderado venta', for sales and purchases alike), while buyRate exists because the Impuesto a la Renta rule splits compra/venta by activo/pasivo. Rates are STRINGS with six decimals and must be parsed as decimals — routing a rate that gets multiplied into money through a binary float is the one place a fiscal figure can change value. Informational, like the padrón route: nothing here validates or blocks a document. Requires consultations:read.
Authorization
bearerAuth Tenant-bound, scoped and expiring Apifact credential. Migrated legacy credentials are accepted only on deprecated v1 writes and tenant-scoped v2 read, poll and download routes. Each operation names the single scope it requires in x-required-scope; the scope array of the security requirement itself is empty because OpenAPI 3.0 requires it to be for a non-oauth2 scheme.
In: header
Path Parameters
The date the rate is to APPLY to — normally the comprobante's issue date. A real calendar date in YYYY-MM-DD: 2026-02-30 is refused with 422 INVALID_DATE rather than rolled over to the 1st of March, which would attach a fiscal figure to a date nobody chose.
^\d{4}-\d{2}-\d{2}$dateQuery Parameters
Three-letter code, case-insensitive and echoed back upper-cased. Defaults to USD, the only series loaded today. A currency with no loaded series answers 503 rather than falling back to USD.
^[A-Za-z]{3}$"USD"Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X GET "https://example.com/api/v2/consultations/exchange-rate/2019-08-24" \ -H "Authorization: Bearer apf_v2_tu_credencial"{ "schemaVersion": "2.0", "requestId": "d385ab22-0f51-4b97-9ecd-b8ff3fd4fcb6", "currency": "string", "requestedOn": "2019-08-24", "rateDate": "2019-08-24", "buyRate": "string", "sellRate": "string", "source": "string", "fallback": true, "stalenessDays": 0}Look a RUC up in the hosted SUNAT padrón — informational, never a validation GET GET
Answers what SUNAT's published padrón says about a RUC: the legal name, the literal estado del contribuyente and condición de domicilio, and a coarse classification of each. THIS DOES NOT VALIDATE ANYTHING AND DOES NOT AFFECT ISSUANCE. A RUC this route reports as not registered can still be invoiced, and doing so is neither an error nor a warning: no create, no trigger and no dispatch decision reads this catalogue. The copy is a snapshot and is therefore ALWAYS behind — a company registered since the snapshot was cut is absent from it while being entirely able to receive a comprobante — so treat the answer as information for a human, not as a gate. THERE ARE THREE OUTCOMES AND ONLY TWO OF THEM ARE ABOUT THE TAXPAYER: 200 with registered=true, 200 with registered=false (a snapshot is loaded and this RUC is not in it), and 503 TAXPAYER_REGISTRY_UNAVAILABLE (no snapshot is loaded at all, which is a statement about this service). A client that folds the 503 into registered=false reports an outage of ours as a fact about somebody's customer. Both the literal SUNAT value and the classification are returned: the literal is authoritative and the classification is ours and may be UNKNOWN, so a value SUNAT adds tomorrow is visible rather than silently mapped. registry.publishedOn dates the snapshot the answer came from and is SUNAT's own publication date, never the time this API ingested it. Requires consultations:read, which is disjoint from every documents:* capability: asking who a RUC belongs to must not require a credential that can issue a comprobante.
Resolve a DNI to a person's NAME — informational, never a validation GET GET
Answers what RENIEC's registry holds for an eight-digit DNI: the person's names, and nothing else. Informational like the two routes above — no create, no trigger and no dispatch decision resolves a DNI, so a boleta whose acquirer name was typed by hand is issued exactly as before while this route is answering 503. ONLY THE NAME IS RETURNED, AND THAT IS A DECISION RATHER THAN A GAP. The directory this API calls also publishes a person's date of birth and gender, on a different endpoint. That endpoint is never called. Neither datum has a fiscal purpose in this product — a boleta needs at most the acquirer's name — and under Ley 29733's minimisation principle obtaining data with no purpose is the wrong default, not a feature waiting to be finished. Asking for them would be a change to this published contract, not a field somebody appends to a mapper. AND NOTHING IS STORED: not the name, not the DNI, and not a hash of the DNI. An eight-digit space is exhaustible in seconds, so a digest of a DNI identifies the person exactly as well as the DNI does and is not a de-identification of anything. What is recorded is one metered row per consultation carrying the tenant, the verdict CLASS and the instant, because the platform pays a third party for each call and an unattributable spend is one nobody can bill or cap. If you need the name later, keep it yourself, and keep `consultedAt` beside it — this surface has no cache and will not serve you the same answer twice. AN ABSENT ANSWER IS NOT AN ABSENT PERSON, and this is the expensive mistake the route is shaped to prevent. There are two answers and seven non-answers. `found: true` and `found: false` are both 200 and are both statements about the person: `false` means the registry was reached, answered, and holds no such record. Everything else is a 502 or a 503 with its own code and is a statement about the network or about this platform — never about the person. 502 DNI_DIRECTORY_AMBIGUOUS is the one to read twice: it means the directory answered 200 while declaring a failure WITHOUT saying which, so this API refuses to guess that the person does not exist. Treating any of the seven as `found: false` writes our outage into somebody's customer record and refuses a real person a real document. Branch on the STATUS CODE first and on `found` second. Requires consultations:dni, which is NOT consultations:read and is not implied by it. The padrón and exchange-rate routes read free local catalogues about legal entities; this one spends a platform contract, writes a row under row-level security and returns personal data about a natural person. A credential handed to a form so it can prefill a company's legal name must not thereby become a credential that can enumerate people's names.