API de códigos QR
El código QR para tus facturas, en una llamada.
Envía el texto o el enlace y recibe solo la imagen del código QR, en PNG o SVG, lista para poner en tu factura, ticket o etiqueta. Un código por llamada, no se guarda nada.
Gratis · 1000 códigos al mes · SVG o PNG
Listo en tres pasos.
Sin tarjeta, sin instalar nada.
- Entrar con Google1
Entra con Google
Gratis, sin tarjeta y sin instalar nada.
- Tu clave · se muestra una vezqs_live_7hK2…Copiar2
Crea tu clave
Un clic en la pestaña Desarrollador de tu perfil. Solo guardamos una huella.
- POST3
Llama y usa la imagen
Recibes solo la imagen del código y cuántos te quedan. El documento sigue siendo tuyo.
Míralo antes de programar.
Elige y mira el código sobre un documento de ejemplo, a su tamaño real. Justo debajo, la llamada ya escrita.
Lo esencial, de un vistazo.
La clave se queda en tu servidor
Va en la cabecera Authorization. Nunca en un navegador ni en una app.
- 6201000
1000 códigos al mes
Vuelve a empezar el día 1. Hasta 30 llamadas por minuto, un código cada una.
La marca aparece sola
Un enlace de WhatsApp lleva el logotipo de WhatsApp. O elige la tuya, o ninguna.
- +52
59 tipos de contenido
Los mismos del editor, con las mismas reglas de validación.
Tu estilo, guardado
Diséñalo en tu perfil y úsalo por su id, o escríbelo en la llamada.
- 400 · content.ssid: required429 · quota-exceeded500 · render-failed
Errores que dicen qué pasa
Un código para tu programa, una frase para quien lo lea. Un error no gasta cuota.
Tres direcciones, nada más.
Todas en https://api.qrsensei.com, con tu clave en la cabecera Authorization. Las respuestas son siempre JSON.
POST/v1/qr
Cuenta 1 código de tu mes (1000)
Genera un código: envía el contenido (y, si quieres, marca, estilo, formato y tamaño) y recibe la imagen como data URL, con lo que te queda.
{ "image": "data:image/png;base64,…", "format": "png", "remaining": 902, "limit": 1000, "resetsAt": "…" }GET/v1/usage
No cuenta
Cuánto has usado y cuánto te queda este mes, sin gastar nada. Útil antes de una tanda grande de facturas.
{ "used": 98, "remaining": 902, "limit": 1000, "resetsAt": "…" }GET/v1/brands
No cuenta
Las marcas que puedes nombrar en brand. Público, sin clave y en caché.
{ "brands": ["qrsensei", "instagram", "whatsapp", …], "auto": "auto", "none": "none" }
Preguntas frecuentes
¿De verdad es gratis? ¿Cuántos códigos puedo generar?
Sí. Cada cuenta tiene 1000 códigos al mes, todas las aplicaciones juntas, y el contador vuelve a empezar el día 1. La cifra actual se muestra siempre aquí y en tu perfil.
¿Guardáis el contenido de mis códigos?
No. El contenido va en el cuerpo de la petición, se dibuja y se devuelve; nada se guarda ni queda en los registros. De tu lado solo guardamos un recuento por mes.
¿PNG o SVG?
SVG siempre que tu PDF o página lo acepte: nítido a cualquier tamaño y más ligero. PNG para sistemas que solo aceptan imágenes; se dibuja al tamaño pedido.
¿Qué tamaño usar en una factura?
Entre 2 y 3 cm impresos, es decir, un size de 256 a 384 a 300 ppp. Por debajo de 1,5 cm muchos móviles dudan. El laboratorio muestra el tamaño real sobre el papel.
¿Puedo llamar a la API desde el navegador o una app?
No: la clave quedaría a la vista de cualquiera. Llama desde tu servidor y envía la imagen ya hecha al navegador o a la app.
¿Cómo pongo la imagen en un PDF?
La respuesta trae la imagen como data URL. En un generador de PDF desde HTML, úsala directamente en un <img>. En librerías de PDF, toma el base64 tras la coma, decodifícalo e insértalo como imagen.
¿El código se leerá siempre?
Cada estilo se comprueba antes de aceptarse (formas que combinan, contraste mínimo de 4,5:1) y la corrección de errores se elige según el tamaño y el logotipo. Pruébalo en el laboratorio: lee el código como un móvil.
¿Qué pasa si supero el límite?
La API responde 429 (quota-exceeded) con la fecha en que vuelve a empezar, y no se cobra nada. Las llamadas con error nunca cuentan, y GET /v1/usage te dice cuánto queda.
¿Puedo poner mi marca en el centro?
Puedes usar la marca reconocida por el enlace ("auto"), una del catálogo, la de QrSensei o ninguna. Los logotipos propios aún no se pueden enviar por la API.
¿Qué pagos puede llevar el código?
Cualquier enlace de pago (Stripe, PayPal, Mercado Pago…) como enlace, y los formatos que los bancos leen directamente: SEPA (EPC, en Europa) y PIX (en Brasil).
¿La API puede cambiar y romper mi sistema?
No sin aviso. Todo está en /v1: lo que funciona hoy sigue igual; las novedades llegan como campos opcionales.
Referencia completa: todos los campos, tipos y errores
Generar un código
Un POST con el contenido en el cuerpo. El contenido nunca va en la dirección, para que no quede en los registros de los servidores por el camino.
curl https://api.qrsensei.com/v1/qr \
-H "Authorization: Bearer $QRSENSEI_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": {
"type": "text",
"text": "FT 2026/118 · 149,90 EUR"
},
"brand": "qrsensei",
"style": {
"color": {
"kind": "solid",
"color": "#18181B"
}
},
"format": "png",
"size": 256
}'Cuerpo de la petición
| Campo | Valores | Qué es |
|---|---|---|
| content | objeto | Lo que guarda el código, con su type. Ver Contenidos. |
| format | "svg" · "png" | SVG por defecto: nítido a cualquier tamaño y más ligero. PNG para sistemas que solo aceptan imágenes. |
| size | 128 – 1024 | El lado de la imagen en píxeles (512 por defecto). Para una factura, 256 basta. |
| style | id · objeto | Uno de tus estilos por su id, o un estilo escrito en la llamada. Sin él: el estilo de la aplicación, o el predeterminado de la cuenta. |
| brand | "auto" · "none" · … | El logotipo en el centro: reconocido por el enlace (auto), el nombre de una marca, o ninguno. |
Respuesta
{
"image": "data:image/png;base64,iVBORw0KGgo…",
"format": "png",
"remaining": 902,
"limit": 1000,
"resetsAt": "2026-12-01T00:00:00.000Z"
}Los mismos números vienen en las cabeceras X-QrSensei-Remaining, X-QrSensei-Limit y X-QrSensei-Reset.
Autenticación
Cada llamada lleva la clave de tu aplicación en la cabecera Authorization: Bearer qs_live_….
- La clave se muestra una vez, al crearla. Solo guardamos una huella: si la pierdes, crea una nueva en tu perfil y la antigua deja de funcionar.
- Llama desde tu servidor, nunca desde un navegador o una app: una clave en el código del front-end la puede leer cualquiera.
- No se guarda nada de los códigos: ni el contenido ni las imágenes. Solo un recuento por mes.
Contenidos
Los mismos tipos que el editor, con las mismas reglas. Esta tabla se lee de las reglas que comprueban cada llamada: no puede quedarse desactualizada.
| type | Obligatorios | Opcionales |
|---|---|---|
| url | url | — |
| text | text | — |
| phone | phone | — |
| subject, body | ||
| sms | phone | message |
| wifi | ssid, password | encryption, hidden |
| vcard | firstName | lastName, organization, title, phone, email, website, address, note |
| raw | payload | — |
| phone | message | |
| telegram | usernameOrHandle | message |
| signal | recipient | message |
| viber | recipient | message |
| mecard | name | organization, phone, email, website, address, note |
| geo | latitude, longitude | label |
| googleMaps | — | latitude, longitude, query |
| waze | — | latitude, longitude, query |
| event | title, start, end | location, description, allDay |
| pix | pixKey, merchantName, merchantCity | amount, txid, description |
| epc | beneficiaryName, iban | bic, amount, reference, remittance |
| paypal | paypalHandleOrEmail | amount, currency, note, paymentUrl |
| mbway | phone | amount, note |
| cryptoWallet | address | currency, amount, network, memo |
| deepLink | uri | — |
| otp | issuer, accountName, secret | otpType, algorithm, digits, period, counter |
| auth | uri | — |
| structuredText | text | — |
| paramUrl | baseUrl | params |
| customContent | content | — |
| enlaces | url | calendarLink · instagram · facebook · linkedin · x · tiktok · youtube · github · spotify · snapchat · pinterest · threads · discord · appStore · googlePlay · appGallery · microsoftStore · pdf · fileDownload · onlineDocument · portfolio · menu · digitalCard · coupon · landingPage · form · review · checkIn · rsvp · catalog · credentialLink |
Una Wi-Fi sin contraseña lleva encryption "nopass". Un campo omitido queda vacío; un campo obligatorio que falta se nombra en el detail del error.
Estilos
Diseñados en tu perfil, con el código en vivo al lado. Cada uno tiene un id y uno es tu predeterminado; o escribe el estilo en la propia llamada, como style.
{}{ "color": { "kind": "solid", "color": "#18181B" }, "background": { "kind": "transparent" }, "quietZone": 2 }{ "dots": "dots", "finders": "drop", "color": { "kind": "gradient", "type": "linear", "angle": 45, "stops": [ "#0F172A", "#1E40AF" ] }, "background": { "kind": "solid", "color": "#FFF7ED" } }Un estilo solo se acepta si se lee: formas que combinan, y tinta más oscura que el papel con un contraste de al menos 4,5:1. Un código transparente se comprueba contra blanco, el papel de una factura.
Marcas
"auto" reconoce la marca por el enlace (un enlace de Instagram lleva el logotipo de Instagram). Un nombre fija la marca; "none" deja el centro libre. La lista también viene de GET /v1/brands.
Contra el phishing: en un enlace, una marca fija solo se usa si el enlace es de esa marca. El logotipo de un banco nunca acaba en un enlace falso.
- qrsensei
- x
- tiktok
- youtube
- github
- spotify
- telegram
- signal
- viber
- snapchat
- threads
- discord
- messenger
- googleMaps
- appleMaps
- waze
- paypal
- revolut
- wise
- stripe
Límites
| Por mes | 1000 códigos por cuenta, todas las aplicaciones juntas. Vuelve a empezar el día 1, a las 00:00 UTC. |
| Por minuto | 30 llamadas por aplicación. Un código por llamada: no hay generación masiva. |
| No cuenta | Llamadas con error, GET /v1/usage y GET /v1/brands. |
Errores
Siempre un objeto error con code, message y, cuando ayuda, detail: el code es para tu programa, el message para quien lo lea. Una llamada con error no se cuenta.
| Estado | code | Cuándo |
|---|---|---|
| 401 | missing-key | No hay clave en la cabecera Authorization. |
| 401 | invalid-key | La clave no existe o fue sustituida. |
| 400 | invalid-json | El cuerpo no es JSON. |
| 400 | invalid-request | Un campo fuera de la lista, o un valor fuera de rango; detail lo indica. |
| 400 | invalid-content | Falta un campo obligatorio o es incorrecto; detail lo indica. |
| 400 | unknown-brand | Esa marca no está en GET /v1/brands. |
| 422 | brand-mismatch | Una marca fija en un enlace que no es suyo. |
| 404 | unknown-style | Ese id de estilo no es de tu cuenta. |
| 403 | not-developer | El modo desarrollador está desactivado en la cuenta. |
| 403 | suspended | La cuenta no puede usar la API ahora. |
| 413 | too-large | El cuerpo supera los 16 KB. |
| 429 | quota-exceeded | Se acabaron los códigos de este mes. |
| 429 | rate-limited | Demasiadas llamadas este minuto; Retry-After dice cuánto esperar. |
| 500 | render-failed | No se pudo dibujar el código. No cuenta. |
| 503 | api-off | La API está apagada un rato. No cuenta. |