API Reference

# Verify a CFDI with SAT

Ask SAT whether a CFDI (factura) is registered, current, or cancelled, by folio fiscal or by uploading its XML.

POST

`/v1/cfdi/verify`

Ask SAT whether a CFDI (factura) is registered in its records, and whether it is still current (vigente) or was cancelled. The answer comes from SAT's Verifica CFDI portal and includes whether the CFDI can be cancelled, its cancellation status, and the cancellation date and reason when it was cancelled. Completes in roughly 3 seconds.

Use before paying a supplier invoice or deducting it, to confirm a CFDI a customer sent you, or to re-check stored facturas for cancellations.

## Request body

Send either the folio search (uuid, rfc_emisor, and rfc_receptor) or the CFDI itself as xml, never both. A request that mixes them is rejected with 422.


| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `uuid` | string | For a folio search | The folio fiscal (UUID) from the CFDI's timbre. Case does not matter. |
| `rfc_emisor` | string | For a folio search | RFC of the issuer (emisor). |
| `rfc_receptor` | string | For a folio search | RFC of the recipient (receptor). |
| `xml` | string | Instead of the folio fields | The CFDI XML, uploaded as a file (multipart/form-data) or sent as a string in a JSON body. SAT reads the folio and RFCs from it and also checks the CFDI's seals, returned as sello_cfdi and sello_timbre_fiscal. |


## Example request: by folio

curl

```bash
curl -X POST https://rfccheck.com/v1/cfdi/verify \
  -H "x-api-key: rfc_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "uuid": "5FB2822E-396D-4725-8521-CDC4BDD20CCF",
    "rfc_emisor": "ACM010101AAA",
    "rfc_receptor": "XAXX010101000"
  }'
```

## Example request: by XML

Upload the XML file as multipart/form-data, the way a browser form sends a file.

curl

```bash
curl -X POST https://rfccheck.com/v1/cfdi/verify \
  -H "x-api-key: rfc_your_api_key" \
  -F xml=@factura.xml
```

A JSON body works too, with the XML as a string: {"xml": "<?xml version=..."}. Most HTTP libraries escape the quotes and newlines for you.

## Response (200)

200 OK

```json
{
  "uuid": "5FB2822E-396D-4725-8521-CDC4BDD20CCF",
  "found": true,
  "status": "vigente",
  "message": "SAT reports this CFDI is current (vigente).",
  "cfdi": {
    "uuid": "5FB2822E-396D-4725-8521-CDC4BDD20CCF",
    "rfc_emisor": "ACM010101AAA",
    "nombre_emisor": "ACME CONSULTORES",
    "rfc_receptor": "XAXX010101000",
    "nombre_receptor": "PUBLICO EN GENERAL",
    "fecha_emision": "2026-05-06T17:45:57",
    "fecha_certificacion": "2026-05-06T17:46:06",
    "rfc_pac": "AAA010101AAA",
    "total": "$1,000.00",
    "efecto_comprobante": "Ingreso",
    "estado": "Vigente",
    "es_cancelable": "Cancelable sin aceptación",
    "estatus_cancelacion": null,
    "fecha_cancelacion": null,
    "motivo": null,
    "folio_sustitucion": null,
    "sello_cfdi": null,
    "sello_timbre_fiscal": null
  }
}
```

The cfdi fields are SAT's text as the portal prints it: total keeps its currency format, dates have no time zone, and empty cells are null. sello_cfdi and sello_timbre_fiscal are only filled when you sent the xml; a folio search does not see the seals.

## found and status


| `found` | `status` | Meaning |
| --- | --- | --- |
| `true` | `vigente` | SAT has the CFDI and it is current. |
| `true` | `cancelado` | SAT has the CFDI and it was cancelled. See estatus_cancelacion, fecha_cancelacion, motivo, and folio_sustitucion. |
| `false` | `no_encontrado` | SAT has no CFDI with that folio and those RFCs. message carries SAT's own explanation, in Spanish. A CFDI stamped in the last 72 hours may not be registered yet. |
| `false` | `rechazado` | The portal refused the request itself, for example an XML it could not read. message carries the portal's reason. |
| `null` | `null` | SAT's portal could not be reached. Retry later. |


## Billing

Each request costs 5 credits. Answers with found: null and 422 errors are not charged. A no_encontrado answer is charged, because SAT answered the question you asked.

## Errors

Missing or malformed fields are rejected with 422 invalid_fields, listing every field to fix at once. Sending both the folio fields and xml lists the folio fields you sent plus xml.

422

```json
{
  "error": "invalid_fields",
  "fields": ["uuid", "rfc_receptor"]
}
```