Documentación API
API REST para integrar todas las herramientas de herramient.as en tus aplicaciones
Inicio Rápido
1. Obtén tu API Key
Inicia sesión con Google y genera una clave API desde tu panel de control.
2. Realiza tu primera llamada
curl -X POST https://herramient.as/api/v1/tools/qr/generate \
-H "Authorization: Bearer tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"content": "https://herramient.as", "format": "png", "size": 512}'
3. Procesa la respuesta
{
"id": "file_abc123",
"download_url": "https://herramient.as/files/abc123.png",
"expires_at": "2026-10-04T15:00:00Z"
}
Autenticación
Todas las peticiones a la API deben incluir tu clave API en la cabecera Authorization:
Authorization: Bearer htas_tu_clave_api_aqui
Importante
Nunca compartas tu clave API públicamente ni la incluyas en código del lado del cliente. Mantén tus claves seguras y rótalas periódicamente.
Límites de Uso
Si excedes estos límites, recibirás una respuesta 429 Too Many Requests.
La respuesta incluirá una cabecera Retry-After indicando cuándo puedes reintentar.
Planes de API
El plan gratuito es el de arriba. Si necesitas más caudal o más unidades por periodo, estos son los planes y lo que incluye cada uno.
| Plan | Precio | Por minuto | Por hora | Unidades/periodo |
|---|---|---|---|---|
| Gratuito Para probar la API y para proyectos personales. | Gratis | 20 | 500 | 2.000 |
| Starter Para un formulario o un alta de clientes en producción. | €19/mes | 60 | 1.000 | 10.000 |
| Growth Para volumen sostenido o varias integraciones. | €59/mes | 120 | 5.000 | 100.000 |
| Business Caudal y cuota negociados; se configuran en tu workspace. | A medida | A medida | A medida | Sin límite por plan |
Por encima del plan gratuito puedes activar el exceso de consumo a €2,50/1.000 unidades, siempre con un tope que pones tú.
La API y el plan PRO son suscripciones distintas. PRO mejora las herramientas de ficheros en la web y no incluye cuota de API; estos planes no incluyen PRO.
Cuota por periodo
Aparte del caudal, cada workspace tiene una cuota de 2.000 unidades por periodo de facturación en el plan gratuito. Todas las API keys de un workspace consumen la misma cuota: las claves son credenciales, y lo que se contrata es la capacidad del workspace.
La mayoría de los endpoints cuesta 1 unidad. Los que hacen más trabajo cuestan más, porque no cuesta lo mismo validar un DNI que consultar el Catastro:
- Validadores, generadores, hipoteca, comparar textos: 1 unidad
- Leer metadatos o descodificar un QR: 2 unidades
- Comprimir, redimensionar o recortar imagen, y generar QR: 3 unidades
- Consultas al Catastro (referencia, búsqueda y plano): 5 unidades
- Extraer texto por OCR: 10 unidades
- Consultar el estado o el resultado de un trabajo: gratis
Al agotarla recibirás un 402 Payment Required
—no un 429— con lo consumido, la fecha de renovación y dónde ampliar el plan.
Las peticiones que fallan no consumen cuota, y el tráfico de los formularios de
la web tampoco: solo se mide lo que llega con una API key. Las respuestas
autenticadas traen cabeceras X-Quota-*
para que puedas verlo venir.
Especificación OpenAPI
Toda la API está descrita en una especificación OpenAPI 3.1 que se genera
a partir del código, así que nunca se queda desactualizada. Impórtala en Postman, Insomnia
o Bruno, o genera un cliente para tu lenguaje con openapi-generator.
# Generar un cliente a partir de la especificación npx @openapitools/openapi-generator-cli generate \ -i https://herramient.as/api/v1/openapi.json \ -g python \ -o ./cliente-herramientas
La especificación es pública y no consume cuota: no necesitas una API key para descargarla.
Endpoints Disponibles
Herramientas de fichero: lo que comparten
-
El fichero se manda como
multipart/form-dataen el campofile, con un máximo de 25 MB. - No almacenamos nada. El fichero se procesa y se responde; no hay URL de descarga ni expiración porque no hay fichero que expirar. Las tres que devuelven una imagen la devuelven en el cuerpo de la respuesta.
- El procesamiento ocurre en un contenedor aislado, sin red y sin credenciales, aparte del que atiende esta API. Es la razón de que un fichero malicioso no pueda llegar a nada.
-
422 frente a 503, que es la distinción que conviene programar: un
422significa que el fichero es el problema y reintentarlo dará lo mismo; un503significa que el problema es nuestro, llega conRetry-Aftery sí merece reintento.
Para lo que no cabe en una petición HTTP: reconocer el texto de un escaneo y convertir vídeo tardan minutos, no milisegundos. Se crea un trabajo, se ejecuta en segundo plano y se recoge el resultado después.
Un solo recurso para todas las operaciones largas. Lo que separa un trabajo asíncrono de uno sincrónico es cuánto tarda, no qué hace, así que no hay una API por herramienta: la siguiente operación lenta usará estos mismos tres endpoints.
expires_at.
/api/v1/jobs
Parámetros
| operation | string | documents.ocr, video.compress o video.convert. Obligatorio |
| file | file | El fichero. Obligatorio, máximo 500 MB |
| webhook_url | string | URL pública a la que avisar. Tiene que ser http o https y no puede apuntar a una dirección interna |
| language | string | Solo OCR: spa, eng, cat, glg, eus, por, fra, deu o ita (por defecto: spa) |
| max_pages | integer | Solo OCR: de 1 a 50 (por defecto: 50) |
| format | string | Obligatorio en vídeo: mp4, mov, mkv, webm, gif, mp3, m4a, wav u ogg |
| quality | string | Vídeo: alta, media o baja (por defecto: media) |
| height | integer | Vídeo: alto máximo, de 144 a 2160. No agranda |
| mute | boolean | Vídeo: quita la pista de audio |
curl -X POST https://herramient.as/api/v1/jobs \ -H "Authorization: Bearer htas_tu_clave_api" \ -F "operation=video.convert" \ -F "file=@grabacion.mov" \ -F "format=mp4" \ -F "height=720" \ -F "webhook_url=https://tu-app.example/hooks/htas"
HTTP/1.1 202 Accepted
{
"id": "6f1e9c2a-1b3d-4e5f-8a9b-0c1d2e3f4a5b",
"operation": "video.convert",
"status": "queued",
"progress": 0,
"expires_at": "2026-10-04T15:00:00Z"
}
/api/v1/jobs/:token
La fuente de verdad del estado, con webhook o sin él. Mientras status sea
queued o running, espera unos segundos entre consultas: un
transcode de cinco minutos sondeado cada dos segundos son 150 peticiones para decir
"sigue en curso", y para eso está el webhook.
{
"id": "6f1e9c2a-...",
"status": "done",
"progress": 100,
"result": { "format": "mp4", "bytes": 482112 },
"result_url": "https://herramient.as/api/v1/jobs/:token/result",
"expires_at": "2026-10-04T15:00:00Z"
}
/api/v1/jobs/:token/result
Responde con los bytes del fichero. Los distintos "todavía no" tienen cada uno su código, para no obligar a adivinar:
| 202 | Sigue en curso. Lleva Retry-After |
| 204 | Esta operación no produce fichero: el OCR devuelve texto, y está en el estado |
| 410 | El resultado caducó y se borró |
| 422 | El trabajo falló; el motivo está en el estado |
curl -H "Authorization: Bearer htas_tu_clave_api" \ https://herramient.as/api/v1/jobs/:token/result \ -o convertido.mp4
El webhook
Si pasas webhook_url, al terminar hacemos un POST a esa URL.
Es una cortesía, no la fuente de verdad: si falla, el estado sigue
consultable. Se reintenta tres veces, al minuto, a los cinco y a la media hora.
El contenido no viaja en el aviso — ni el texto reconocido ni el fichero. Un webhook va por la red a un destino que elegiste tú, y mandar ahí el texto de una nómina sería regalar el dato en lugar de avisar de que está listo. El aviso dice dónde recogerlo; lo recoges con tu clave.
POST /hooks/htas
X-Herramientas-Event: tool_job.finished
X-Herramientas-Signature: sha256=3f9a...
{
"event": "tool_job.finished",
"job": {
"id": "6f1e9c2a-...",
"operation": "video.convert",
"status": "done",
"result_url": "https://herramient.as/api/v1/jobs/:token/result",
"expires_at": "2026-10-04T15:00:00Z"
}
}
Verifica la firma. X-Herramientas-Signature es un
HMAC-SHA256 del cuerpo crudo con un secreto derivado de tu clave de API.
Compáralo sobre los bytes tal como llegan, no sobre el JSON reparseado: dos
serializaciones del mismo objeto pueden diferir en el orden de las claves y la firma
dejaría de cuadrar.
/api/v1/tools/documents/extract_text
scanned: true; para esos, la página de la herramienta reconoce el texto en
el navegador.
Parámetros
| file | file | El PDF. Obligatorio, máximo 25 MB |
| max_pages | integer | Cuántas páginas leer, de 1 a 200 (por defecto: 200) |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/documents/extract_text \ -H "Authorization: Bearer htas_tu_clave_api" \ -F "file=@factura.pdf" \ -F "max_pages=10"
Respuesta
{
"text": "Factura numero 2026-001\nTotal: 42,50 EUR",
"pages": 1,
"pages_read": 1,
"truncated": false,
"scanned": false,
"characters": 40,
"words": 6
}
/api/v1/tools/text/diff
El único de esta familia que no recibe ficheros: va en JSON y no tiene ni límite de subida ni el 503 de los demás.
Parámetros
| left | string | Texto original. Obligatorio, máximo 200.000 caracteres. Admite cadena vacía |
| right | string | Texto modificado. Obligatorio, mismas condiciones |
| ignore_case | boolean | Mayúsculas y minúsculas cuentan como iguales (por defecto: false) |
| ignore_whitespace | boolean | Colapsa espacios repetidos y recorta los extremos (por defecto: true) |
| ignore_punctuation | boolean | Descarta signos de puntuación (por defecto: false) |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/text/diff \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{
"left": "plazo de treinta días",
"right": "plazo de quince días"
}'
Respuesta
{
"identical": false,
"stats": { "added": 1, "removed": 1, "equal": 0 },
"parts": [
{ "type": "removed", "left": ["plazo de treinta días"], "right": [] },
{ "type": "added", "left": [], "right": ["plazo de quince días"] }
]
}
/api/v1/tools/images/metadata
Parámetros
| file | file | La foto, JPEG o TIFF. Obligatorio, máximo 25 MB |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/images/metadata \ -H "Authorization: Bearer htas_tu_clave_api" \ -F "file=@foto.jpg"
Respuesta
{
"present": true,
"camera": "Apple iPhone 14",
"taken_at": "2026-10-03T11:22:33+02:00",
"gps": { "latitude": 40.416944, "longitude": -3.703889 }
}
present: false es una respuesta válida y no un error: la foto se leyó y no
lleva metadatos. La longitud es negativa al oeste de Greenwich, así que una foto de
Madrid sale con longitud negativa.
/api/v1/tools/images/decode_qr
Devuelve una lista porque una imagen puede traer varios códigos. Lee
también códigos de barras de una dimensión (EAN-13, CODE-128 y los demás que reconoce
ZBar), y type dice de cuál se trata.
Parámetros
| file | file | La imagen. JPEG, PNG, WebP, AVIF o TIFF. Obligatorio, máximo 25 MB |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/images/decode_qr \ -H "Authorization: Bearer htas_tu_clave_api" \ -F "file=@captura.png"
Respuesta
{
"found": true,
"symbols": [
{
"data": "https://herramient.as/leer-codigo-qr",
"type": "QR-Code",
"quality": 1
}
]
}
found: false con symbols: [] es una respuesta válida y no un
error: la imagen se leyó y no había ningún código en ella.
/api/v1/tools/images/compress
Responde con los bytes de la imagen, no con JSON. Las medidas de salida
viajan en las cabeceras X-Image-Width,
X-Image-Height y
X-Image-Bytes. Se eliminan todos los
metadatos, incluida la ubicación GPS.
Parámetros
| file | file | La imagen. Obligatorio, máximo 25 MB |
| format | string | jpeg, png, webp o avif (por defecto: jpeg) |
| quality | integer | De 1 a 100 (por defecto: 80). No aplica a png |
| max_width | integer | Ancho máximo. No agranda una imagen más pequeña |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/images/compress \ -H "Authorization: Bearer htas_tu_clave_api" \ -F "file=@foto.jpg" \ -F "format=webp" \ -F "quality=80" \ -F "max_width=1920" \ -o foto-comprimida.webp
/api/v1/tools/images/resize
Al menos una de width o height. La imagen se mete dentro de
esas medidas manteniendo la proporción y sin agrandarla nunca: si pides
2000 px de una imagen de 800, recibes 800, y las cabeceras dicen qué tamaño salió de
verdad.
Parámetros
| file | file | La imagen. Obligatorio, máximo 25 MB |
| width | integer | Ancho máximo, de 1 a 10000 |
| height | integer | Alto máximo, de 1 a 10000 |
| format | string | jpeg, png, webp o avif (por defecto: jpeg) |
| quality | integer | De 1 a 100 (por defecto: 90) |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/images/resize \ -H "Authorization: Bearer htas_tu_clave_api" \ -F "file=@foto.jpg" \ -F "width=1080" \ -o foto-1080.jpg
/api/v1/tools/images/crop
width y height son obligatorios: la medida exacta es lo que
distingue un recorte de un redimensionado. La imagen rellena esa caja y lo que sobra se
recorta desde el centro. Para las medidas de cada red social,
la página de la herramienta
las trae ya puestas.
Parámetros
| file | file | La imagen. Obligatorio, máximo 25 MB |
| width | integer | Ancho exacto de salida. Obligatorio |
| height | integer | Alto exacto de salida. Obligatorio |
| format | string | jpeg, png, webp o avif (por defecto: jpeg) |
| quality | integer | De 1 a 100 (por defecto: 90) |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/images/crop \ -H "Authorization: Bearer htas_tu_clave_api" \ -F "file=@foto.jpg" \ -F "width=1080" \ -F "height=1350" \ -o instagram-vertical.jpg
/api/v1/tools/qr/generate
Por defecto el código sale con módulos redondeados y la firma «creado con herramient.as»
debajo; mándale caption=false para quitarla. Con logo, la
petición va en multipart/form-data y la corrección de errores
sube al nivel H, así que cabe menos texto.
Parámetros
| content | string | Contenido del código QR (URL, texto, etc.). Máximo 2.000 caracteres |
| format | string | "png" o "svg" (por defecto: "png") |
| size | integer | Ancho: 256, 512, 1024 o 2048 px (por defecto: 512). Con firma, el alto es un 8 % mayor |
| style | string | "square", "rounded" o "dots" (por defecto: "rounded") |
| eyes | string | Forma de las esquinas: "square", "rounded" o "circle" (por defecto: "rounded") |
| color | string | Color del código, como "#1e3a8a" (por defecto: "#111827") |
| eye_color | string | Color de las esquinas (por defecto: el de color) |
| background | string | Color del fondo (por defecto: "#ffffff") |
| caption | boolean | Firma debajo del código (por defecto: true) |
| icon | string | Icono para el centro: "wifi", "whatsapp", "star" o "contact" (opcional; se ignora si mandas logo) |
| logo | file | PNG, JPG o WebP de hasta 5 MB para el centro (opcional) |
| logo_size | string | "small", "medium" o "large" (por defecto: "medium") |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/qr/generate \ -H "Authorization: Bearer htas_tu_clave_api" \ -F "content=https://herramient.as" \ -F "style=dots" \ -F "color=#1e3a8a" \ -F "size=1024" \ -F "logo=@logo.png"
Cada plantilla compone por ti el texto del código, con los escapes de su formato, y lo dibuja
igual que /api/v1/tools/qr/generate: admiten las mismas opciones
de diseño (format, size,
style, eyes, colores,
caption, icon y logo) y devuelven la misma
respuesta, con el texto codificado en content.
Red wifi
/api/v1/tools/qr/wifi
| ssid | string | Nombre de la red (obligatorio) |
| password | string | Contraseña. Obligatoria salvo con security=nopass |
| security | string | "WPA" (WPA, WPA2 y WPA3), "WEP" o "nopass". Por defecto "WPA" |
| hidden | boolean | Si la red es oculta. Por defecto false |
curl -X POST https://herramient.as/api/v1/tools/qr/wifi \ -H "Authorization: Bearer htas_tu_clave_api" \ -F "ssid=MiCasa_5G" -F "password=una-clave-larga"
Chat de WhatsApp
/api/v1/tools/qr/whatsapp
| phone | string | Número (obligatorio). Sin prefijo se entiende +34 |
| message | string | Mensaje que aparece ya escrito al abrir el chat |
curl -X POST https://herramient.as/api/v1/tools/qr/whatsapp \ -H "Authorization: Bearer htas_tu_clave_api" \ -F "phone=600 12 34 56" -F "message=Hola, quiero reservar mesa"
Reseñas de Google
/api/v1/tools/qr/google_review
| link | string | Enlace de «Pedir reseñas» de tu Perfil de Empresa o Place ID (obligatorio). Solo dominios de Google |
curl -X POST https://herramient.as/api/v1/tools/qr/google_review \ -H "Authorization: Bearer htas_tu_clave_api" \ -F "link=https://g.page/r/CabcdEFGH/review"
Tarjeta de contacto (vCard 3.0)
/api/v1/tools/qr/vcard
| first_name | string | Nombre (obligatorio) |
| phone / email | string | Al menos uno de los dos |
| last_name, organization, title, website, street, postal_code, city | string | Opcionales |
curl -X POST https://herramient.as/api/v1/tools/qr/vcard \ -H "Authorization: Bearer htas_tu_clave_api" \ -F "first_name=Lucía" -F "last_name=García" -F "phone=600 12 34 56"
/api/v1/tools/iban/validate
Parámetros
| iban | string | Código IBAN a validar |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/iban/validate \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"iban": "ES9121000418450200051332"}'
/api/v1/tools/dni/validate
Parámetros
| id | string | DNI, NIF o NIE a validar |
/api/v1/tools/cif/validate
Parámetros
| cif | string | CIF de la entidad a validar |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/cif/validate \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"cif": "A28015865"}'
Devuelve valid, el tipo de entidad en
details.entity_key y si el carácter de control
es una letra o un dígito en details.control_type.
/api/v1/tools/ccc/to_iban
Parámetros
| ccc | string | Código Cuenta Cliente de 20 dígitos |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/ccc/to_iban \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"ccc": "21000418450200051332"}'
Valida los dos dígitos de control del CCC y, solo si son correctos, devuelve el
IBAN en details.iban.
/api/v1/tools/naf/validate
Parámetros
| naf | string | Número de afiliación (NAF o NUSS) de 12 dígitos |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/naf/validate \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"naf": "081304526390"}'
Los dos dígitos de control son el resto de dividir entre 97 los diez primeros dígitos,
provincia primero. Comprobamos además que la provincia exista, así que
checksum_valid puede ser true
y valid ser false si el código
de provincia no está asignado.
/api/v1/tools/catastral/lookup
Parámetros
| reference | string | Referencia catastral de 20 caracteres, o parcela de 14 |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/catastral/lookup \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"reference": "9872023VH5797S0001WX"}'
Resuelve la referencia contra los servicios web libres de la Dirección General del Catastro y devuelve los datos no protegidos del inmueble: dirección, uso, superficie construida, año de construcción, desglose de construcciones y coordenadas. La titularidad no se consulta: es un dato protegido que requiere certificado digital.
A eso se añaden, desde los servicios INSPIRE del Catastro,
plot_area_m2 (la parcela),
built_on_plot_m2 (todo lo edificado sobre ella),
buildings_count, dwellings
y plan, con la URL del plano, su
bbox y el contorno de la parcela.
Las tres superficies miden cosas distintas y no se suman.
built_area_m2 es la del inmueble que has consultado;
plot_area_m2 es la del solar, que en un edificio de
vecinos está compartido; built_on_plot_m2 es la suma de
todo lo construido encima. Ninguna es la superficie útil, que el Catastro no publica.
Los cuatro campos y plan se omiten cuando no hay
cartografía, lo que incluye Álava, Gipuzkoa, Bizkaia y Navarra.
Los dos caracteres de control los valida el propio Catastro, así que una referencia mal
formada llega como found: false con el motivo en
message. Si el Catastro no responde, devolvemos
503 en lugar de hacer pasar una caída por un
"no existe". Las consultas resueltas se cachean 12 horas, y la cartografía 30 días.
/api/v1/tools/catastral/plano
Parámetros
| reference | string | Referencia catastral de 20 caracteres, o parcela de 14. Obligatorio |
| tamano | string | card (640×420) o wide (1024×420). Por defecto card |
Ejemplo de petición
curl "https://herramient.as/api/v1/tools/catastral/plano?reference=9872023VH5797S0001WX" \ -H "Authorization: Bearer htas_tu_clave_api" \ -o plano.png
Este endpoint responde con una imagen image/png, no con
JSON. Es la cartografía catastral oficial recortada sobre la parcela, con la atribución
a la Dirección General del Catastro impresa en la propia imagen: puedes reutilizarla
siempre que no la recortes.
Hacemos de intermediarios por dos motivos: el servicio WMS del Catastro no envía
cabeceras CORS, así que un navegador no puede leerlo directamente, y el encuadre hay
que calcularlo antes a partir de la geometría de la parcela. Si quieres dibujar encima,
el bbox con el que se ha generado la imagen y el
contorno de la parcela vienen en el campo plan de
/catastral/lookup.
Devolvemos 404 cuando la parcela no tiene cartografía:
una referencia que ya no existe, o los territorios forales de Álava, Gipuzkoa, Bizkaia
y Navarra. Cada plano se cachea 30 días y se indexa por parcela, así que todos los
inmuebles de un mismo edificio comparten una única imagen.
/api/v1/tools/catastral/search
Parámetros
| province | string | Provincia, en la grafía del Catastro. Obligatorio |
| municipality | string | Municipio. Obligatorio |
| street_type | string | Sigla de la vía: CL, AV, PZ, CR... |
| street | string | Nombre de la vía, sin la sigla. Obligatorio |
| number | string | Número de policía. Obligatorio |
| block, stairway, floor, door | string | Bloque, escalera, planta y puerta. Acotan la búsqueda a un solo inmueble |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/catastral/search \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"province": "MADRID", "municipality": "MADRID", "street_type": "CL",
"street": "ARTURO SORIA", "number": "100", "floor": "01", "door": "A"}'
Un número de calle suele agrupar muchos inmuebles, así que la respuesta habitual es una
lista en properties con la referencia, la planta y la
puerta de cada uno. Eso no es un error: es el caso normal. Si añades
floor y door, la respuesta
se concreta en un único inmueble con found: true.
Los nombres tienen que coincidir con la grafía del Catastro, y su endpoint libre no
permite listar el callejero. Para que la consulta se pueda corregir, cuando el Catastro
rechaza una dirección devolvemos su motivo literal en
catastro_error, por ejemplo
LA PROVINCIA NO EXISTE.
El Catastro busca por su propia grafía, y los nombres de vía están normalizados, sin
tildes y a veces con el orden invertido, así que no se pueden deducir del nombre de la
placa. Estos dos endpoints devuelven los nombres reales para que puedas construir una
llamada a /catastral/search que acierte.
/api/v1/tools/catastral/municipios
Parámetros
| province | string | Provincia, en la grafía del Catastro. Obligatorio |
curl "https://herramient.as/api/v1/tools/catastral/municipios?province=MADRID" \ -H "Authorization: Bearer htas_tu_clave_api"
/api/v1/tools/catastral/vias
Parámetros
| province | string | Provincia. Obligatorio |
| municipality | string | Municipio. Obligatorio |
| q | string | Parte del nombre de la vía, mínimo 3 caracteres. Obligatorio |
curl "https://herramient.as/api/v1/tools/catastral/vias?province=MADRID&municipality=MADRID&q=ALCAL" \ -H "Authorization: Bearer htas_tu_clave_api"
Cada vía llega con street_type (la sigla, CL, AV, PZ...),
name (el valor que espera search)
y label para mostrar. Se devuelven como máximo 50
resultados. Álava, Gipuzkoa, Bizkaia y Navarra devuelven listas vacías: tienen catastro
foral propio y no están en este servicio.
/api/v1/tools/identifier/detect
Parámetros
| value | string | Cualquier identificador español: DNI, NIE, CIF, NIF K/L/M o NAF |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/identifier/detect \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"value": "A28015865"}'
Un solo endpoint para cualquier identificador: detecta el tipo
(DNI, NIE,
CIF o NIF),
valida el carácter de control y devuelve el desglose correspondiente.
/api/v1/tools/mortgage/schedule
Parámetros
| capital | number | Importe del préstamo en euros. Obligatorio |
| years | integer | Plazo en años, de 1 a 50. Obligatorio |
| type | string | fixed, variable o mixed. Por defecto fixed |
| rate | number | TIN en porcentaje. En mixed es el tramo fijo |
| euribor | number | Euríbor de referencia, para el tramo variable |
| differential | number | Diferencial sobre el Euríbor, en puntos |
| revision_months | integer | Cada cuántos meses se revisa el tipo. Por defecto 12 |
| fixed_years | integer | Años del tramo fijo, solo en mixed |
| euribor_shock | number | Puntos que se suman al Euríbor desde la primera revisión |
| include_schedule | boolean | Devuelve el cuadro mes a mes. Por defecto false |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/mortgage/schedule \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"capital": 200000, "years": 25, "type": "fixed", "rate": 3.0}'
Sistema francés, con i igual al TIN dividido entre doce,
que es la convención española. El redondeo es a céntimos en cada periodo y la última
cuota absorbe el resto, así que el capital amortizado suma exactamente el prestado.
En variable y mixto la cuota se recalcula en cada revisión sobre el
capital pendiente y el plazo restante, nunca sobre el capital inicial.
euribor_shock aplica los puntos a partir de la primera
revisión y no desde el primer mes, porque el tipo inicial está fijado por contrato
hasta entonces.
effective_annual_rate es solo el efecto de la
capitalización mensual sobre el TIN: no es la TAE y no incluye gastos,
comisiones ni seguros vinculados. Para comparar ofertas, usa la TAE de la FEIN.
/api/v1/tools/mortgage/early_repayment
Parámetros
| balance | number | Capital pendiente en euros. Obligatorio |
| rate | number | TIN actual en porcentaje. Obligatorio |
| remaining_months | integer | Cuotas que quedan por pagar. Obligatorio |
| amount | number | Importe a amortizar, no mayor que el capital pendiente. Obligatorio |
| commission_percent | number | Comisión sobre el importe amortizado. Por defecto 0 |
| investment_return | number | Rentabilidad anual con la que comparar invertir ese dinero. Bruta y antes de impuestos |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/mortgage/early_repayment \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"balance": 150000, "rate": 3.0, "remaining_months": 200, "amount": 20000}'
Devuelve las dos salidas una al lado de la otra: reduce_payment
mantiene la fecha de fin y baja la cuota, reduce_term mantiene
la cuota y acorta el plazo.
Reducir plazo ahorra siempre al menos lo mismo que reducir cuota, porque
deja menos capital pendiente durante menos tiempo. Eso es una identidad aritmética, no un
hallazgo, de modo que verdict solo cambia a
not_worth_it cuando la comisión se come el ahorro entero.
La comisión se cobra sobre el importe amortizado y se suma al desembolso:
el importe completo reduce capital y la comisión es dinero perdido, que es como la cobran
los bancos. Los net_saving vienen ya netos de comisión.
/api/v1/tools/property/purchase_costs
Parámetros
| jurisdiction | string | Código de la jurisdicción fiscal. Obligatorio |
| price | number | Precio de compra en euros. Obligatorio |
| condition | string | second_hand u new_build. Por defecto second_hand |
| reference_value | number | Valor de referencia del Catastro. Si supera el precio, pasa a ser la base imponible |
| declared_value | number | Valor declarado, si difiere del precio |
| consideration | number | Contraprestación pactada, si difiere del valor declarado |
| mortgage_amount | number | Importe de la hipoteca. Determina la entrada y añade la tasación |
| accrual_date | string | Fecha de devengo, AAAA-MM-DD. Selecciona la regla vigente ese día. Por defecto hoy |
| use | string | habitual u other. Requisito de casi todos los tipos reducidos |
| age | integer | Edad de quien compra en la fecha de devengo |
| irpf_qualifying_base | number | Base general más la del ahorro menos el mínimo personal y familiar. No es el sueldo bruto |
| family_category | string | numerosa o monoparental |
| extra_children | integer | Hijos por encima del mínimo de familia numerosa. Amplía el límite de renta |
| disability_percentage | number | Grado de discapacidad en porcentaje |
| gender_violence_victim | boolean | Para el tipo reducido correspondiente |
| municipality | string | Municipio del inmueble. Necesario para los tipos rurales |
| protected_housing | string | regimen_especial o promocion_publica, para el IVA superreducido |
| large_holder | boolean | Si quien compra es gran tenedor. Ausente deja el recargo sin resolver, y se avisa |
| whole_building | boolean | Si se adquiere un edificio residencial completo |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/property/purchase_costs \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"jurisdiction": "cataluna", "price": 300000, "reference_value": 330000,
"mortgage_amount": 240000, "age": 30, "use": "habitual",
"irpf_qualifying_base": 25000, "large_holder": false}'
La base imponible no es el precio. Desde la reforma de 2021 es la mayor de
tres magnitudes —valor declarado, precio pactado y valor de referencia del Catastro— según
el art. 10 del texto refundido del ITPAJD. taxable_base.source
dice cuál de las tres ha ganado, y above_price avisa de que
vas a tributar por más de lo que pagas.
La notaría y el registro se calculan con el arancel oficial
(RD 1426/1989 y RD 1427/1989), con la rebaja del 5 % del RD 1612/2011 y el IVA incluidos.
Se devuelven como horquilla porque el arancel es el suelo de la factura: encima van
copias, folios y suplidos. El campo basis distingue
legal_tariff de market, que es
lo que separa una cifra con respaldo normativo de una estimación de mercado.
El AJD de la escritura de hipoteca lo paga el banco. Desde el
Real Decreto-ley 17/2018 el sujeto pasivo es el prestamista (art. 29), así que se devuelve
en mortgage_ajd con payer
igual a lender y no entra en
cash_needed. Ojo con la confusión habitual: la Ley 5/2019
pone a cargo del banco la gestoría, la notaría y el registro del préstamo, no los
de la compraventa, que siguen siendo tuyos.
Una jurisdicción sin tipo verificado responde 200 con
available: false y el aviso
no_verified_rule, nunca un tipo aproximado ni una media
nacional. No es un error del cliente ni una caída: es una respuesta correcta que dice que
ese dato no lo tenemos. Toda cuota que sí devolvemos viaja con sus
sources y su verified_at.
/api/v1/tools/property/itp
El mismo motor que purchase_costs, recortado al impuesto:
base imponible, tipo, cuota y datos de presentación, sin honorarios ni efectivo necesario.
Acepta los mismos parámetros de perfil
(jurisdiction y price
obligatorios; condition,
reference_value, accrual_date,
use, age,
irpf_qualifying_base,
family_category,
disability_percentage,
municipality y
large_holder opcionales).
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/property/itp \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"jurisdiction": "galicia", "price": 200000}'
En segunda mano devuelve ITP y no AJD: los dos son incompatibles para el
mismo acto (art. 31.2). En obra nueva devuelve el impuesto indirecto que corresponda al
territorio —IVA en la península y Baleares, IGIC en Canarias,
IPSI en Ceuta y Melilla— más el AJD. «Obra nueva = IVA + AJD» no es una
regla universal, y jurisdiction.indirect_tax_regime dice cuál
se ha aplicado.
candidates lleva los tipos reducidos que podrían
aplicarte pero que no se pueden confirmar con lo que has enviado, con el campo que falta y
el ahorro potencial. Un dato ausente nunca se trata como un «no»: eso cobraría de más a
quien no rellenó el formulario entero. trace explica por qué
se aplicó la regla que se aplicó, incluida la precedencia —un recargo de gran tenedor manda
sobre un tipo reducido, no compite con él.
/api/v1/tools/property/comparison
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/property/comparison \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"price": 300000, "age": 30, "use": "habitual", "irpf_qualifying_base": 25000}'
La misma compra y el mismo perfil en cada jurisdicción de la que tengamos el tipo
verificado, ordenadas de menos a más impuesto. No lleva
jurisdiction: las recorre todas. Las que no aparecen van en
unavailable, y eso es un hueco en nuestros datos, no un
territorio sin impuesto.
/api/v1/tools/dni/generate
Parámetros
| type | string | "DNI" o "NIE" (por defecto: "DNI") |
/api/v1/tools/ip
Obtiene información completa sobre la dirección IP del cliente que realiza la petición, incluyendo geolocalización, proveedor de internet, ASN, y detección de VPN/hosting.
Respuesta
| ipv4 | string | Dirección IPv4 detectada |
| ipv6 | string | Dirección IPv6 detectada (si disponible) |
| country | string | País de origen |
| country_code | string | Código ISO del país (ej: "ES") |
| city | string | Ciudad |
| region | string | Región o provincia |
| postal_code | string | Código postal |
| latitude | float | Latitud geográfica |
| longitude | float | Longitud geográfica |
| asn | integer | Número de Sistema Autónomo |
| organization | string | Organización propietaria del ASN |
| isp | string | Proveedor de Internet (ISP) |
| connection_type | string | Tipo de conexión ("Mobile", "Desktop", "Unknown") |
| is_vpn_proxy | boolean | Indica si la IP pertenece a VPN/hosting/datacenter |
| timestamp | string | Marca de tiempo ISO8601 de la consulta |
Ejemplo de petición
curl -X GET https://herramient.as/api/v1/tools/ip \ -H "Authorization: Bearer htas_tu_clave_api"
Ejemplo de respuesta
{
"ipv4": "83.41.167.37",
"ipv6": null,
"country": "Spain",
"country_code": "ES",
"city": "Madrid",
"region": "Madrid",
"postal_code": "28001",
"latitude": 40.4165,
"longitude": -3.7026,
"asn": 3352,
"organization": "Telefónica de España",
"isp": "telefonica.es",
"connection_type": "Desktop",
"is_vpn_proxy": false,
"timestamp": "2025-11-18T12:30:00Z"
}
Nota sobre detección de IP
Este endpoint detecta automáticamente la IP del cliente que realiza la petición, manejando correctamente proxies, CDNs (Cloudflare, etc.) y balanceadores de carga para obtener la IP real del usuario final.
/api/v1/tools/hex_to_rgb
Parámetros
| hex | string | Color en formato #RRGGBB |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/hex_to_rgb \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"hex": "#FF5733"}'
/api/v1/tools/rgb_to_hex
Parámetros
| r | integer | Rojo, de 0 a 255 |
| g | integer | Verde, de 0 a 255 |
| b | integer | Azul, de 0 a 255 |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/rgb_to_hex \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"r": 255, "g": 87, "b": 51}'
/api/v1/tools/uuid
Parámetros
| count | integer | Cuántos UUID generar. Opcional, 1 por defecto, máximo 10 |
Ejemplo de petición
curl -X POST https://herramient.as/api/v1/tools/uuid \
-H "Authorization: Bearer htas_tu_clave_api" \
-H "Content-Type: application/json" \
-d '{"count": 3}'
Devuelve uuids (array) y count.
Se generan UUID v4. Los valores fuera del rango 1–10 se ajustan al límite más cercano
en lugar de devolver error.
Manejo de Errores
La API utiliza códigos de estado HTTP estándar:
200
Petición exitosa
400
Petición incorrecta (parámetros inválidos)
401
No autorizado (clave API inválida)
429
Demasiadas peticiones (límite excedido)
500
Error del servidor
¿Necesitas Ayuda?
Si tienes preguntas sobre la API o necesitas asistencia, no dudes en contactarnos: