Escaneo por API
El escaneo por API convierte un PDF en una copia escaneada realista mediante una llamada REST, ideal para procesos automatizados e integraciones. Crea el trabajo, sube el PDF y consulta el estado o espera el webhook: tres pasos desde cualquier entorno o lenguaje capaz de enviar una petición HTTP. El espacio de color, la resolución, la rotación, el desenfoque, el ruido, el brillo, el contraste y el borde son configurables.
Cómo funciona la llamada
Crear el trabajo
POST /v1/scan-jobs
Envía tu config y, opcionalmente, webhookUrl; recibes un jobID y una uploadURL prefirmada.
Subir el PDF
PUT {uploadURL}
Haz PUT del archivo directamente a la dirección S3 prefirmada del paso anterior: no hace falta token.
Recoger la copia escaneada
GET /v1/scan-jobs/{jobID}
Consulta el estado o espera el webhook; cuando el trabajo esté completed, descárgala desde downloadURL.
Dónde encaja
Producción por lotes desde el backend
Los contratos, las facturas y los informes generados en el servidor pasan directamente por el efecto de escaneo, sin que nadie repita el proceso a mano en la web.
Dentro de un sistema existente
Añade una acción «exportar copia escaneada» a un CRM, un ERP o un sistema de tickets y deja que llame a la API.
Procesos automatizados
CI, n8n, Zapier y similares lanzan un trabajo con un evento, y el webhook da paso al siguiente al terminar.
Grandes colas de archivos
Los trabajos son asíncronos: se crean y cada uno se procesa por su cuenta, con el progreso disponible mediante status y createdAfter.
Lenguajes y entornos
La API es HTTP y JSON estándar, así que cualquier lenguaje o plataforma de automatización capaz de enviar una petición puede usarla.
Ejemplos de código
Add Look Scanned API Scan to this project, so I can turn a PDF into a
realistic scanned copy from code.
API docs: https://lookscanned.io/en/scan/api
Write one function that:
1. POST https://api.lookscanned.io/v1/scan-jobs
Header: Authorization: Bearer $LOOKSCANNED_API_TOKEN
Body: {"config": {"colorspace": "gray", "resolution": 150, "rotate": 1}}
It returns jobID and a presigned uploadURL.
2. PUT the PDF bytes to uploadURL with Content-Type: application/pdf.
Send no Authorization header — that URL is already signed.
3. Poll GET /v1/scan-jobs/{jobID} until status is "completed" (or "failed"),
then return downloadURL.
Read the token from the LOOKSCANNED_API_TOKEN environment variable. Use the
language and HTTP client this project already uses, and add one test.interface ScanConfig {
rotate?: number // degrees to rotate the document
rotate_var?: number // degrees to rotate the document randomly
colorspace?: 'gray' | 'sRGB' // the colorspace of the output image
blur?: number // the amount of blur to apply to the image
noise?: number // the amount of noise to apply to the image
border?: boolean // whether to add a border to the image
brightness?: number // the brightness of the image. 1 is no change
contrast?: number // the contrast of the image. 1 is no change
resolution?: number // the resolution of the image in DPI
output_format?: 'image/png' | 'image/jpeg' // the format of the output image
}
interface ScanOptions {
config: ScanConfig
webhookUrl?: string // webhook URL to notify when job is completed
}
interface ScanResponse {
jobID: string // UUID of the scan job
userID: string // UUID of the user who created the job
createdAt: number // timestamp of job creation
status: 'pending' | 'processing' | 'completed' | 'failed'
config: ScanConfig
inputUploadedAt?: number // timestamp when input file was uploaded
completedAt?: number // timestamp when job was completed
webhookUrl?: string // webhook URL for notifications
uploadURL?: string // S3 presigned URL for file upload
downloadURL?: string // S3 presigned URL for file download
}
async function apiScan(pdfBlob: Blob, scanOptions: ScanOptions, token: string): Promise<ScanResponse> {
const response = await fetch('https://api.lookscanned.io/v1/scan-jobs', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(scanOptions)
})
const result: ScanResponse = await response.json()
// PUT PDF Blob to upload URL
const uploadURL = result.uploadURL
await fetch(uploadURL, {
method: 'PUT',
headers: {
'Content-Type': 'application/pdf',
'Content-Length': pdfBlob.size.toString()
},
body: pdfBlob
})
// get scan job status
const jobStatusResponse = await fetch(`https://api.lookscanned.io/v1/scan-jobs/${result.jobID}`, {
headers: {
'Authorization': `Bearer ${token}`
}
})
return await jobStatusResponse.json()
}import requests
def api_scan(pdf_file, scan_options, token):
# Create scan job
response = requests.post(
'https://api.lookscanned.io/v1/scan-jobs',
headers={'Authorization': f'Bearer {token}'},
json=scan_options
)
result = response.json()
# Upload PDF to presigned URL
upload_url = result['uploadURL']
requests.put(
upload_url,
headers={
'Content-Type': 'application/pdf',
'Content-Length': str(len(pdf_file))
},
data=pdf_file
)
# Get scan job status
job_status = requests.get(
f'https://api.lookscanned.io/v1/scan-jobs/{result["jobID"]}',
headers={'Authorization': f'Bearer {token}'}
)
return job_status.json()
# Example usage
if __name__ == "__main__":
with open('document.pdf', 'rb') as f:
pdf_content = f.read()
options = {
'config': {
# Optional parameters:
# 'rotate': 0, # degrees to rotate the document
# 'colorspace': 'gray', # gray or sRGB
# 'resolution': 300, # DPI
# 'rotate_var': 0, # random rotation variance in degrees
# 'blur': 0, # amount of blur
# 'noise': 0, # amount of noise
# 'border': False, # whether to add border
# 'brightness': 1, # 1 is no change
# 'contrast': 1, # 1 is no change
# 'output_format': 'image/png' # image/png or image/jpeg
},
'webhookUrl': 'https://example.com/webhook'
}
result = api_scan(pdf_content, options, 'your-api-token')
print(f"Scan job created with ID: {result['jobID']}")# Set your API token and PDF file as environment variables
export LOOKSCANNED_API_TOKEN='your_api_token_here'
# Create a new scan job
curl -X POST 'https://api.lookscanned.io/v1/scan-jobs' \
-H "Authorization: Bearer ${LOOKSCANNED_API_TOKEN}" \
-H 'Content-Type: application/json' \
-d '{
"config": {
"rotate": 0,
"rotate_var": 1,
"colorspace": "gray",
"blur": 0.2,
"noise": 0.1,
"border": true,
"brightness": 1.0,
"contrast": 1.0,
"resolution": 300,
"output_format": "image/jpeg"
},
"webhookUrl": "https://your-domain.com/webhook"
}'
# Response will include uploadURL and jobID
# {
# "jobID": "550e8400-e29b-41d4-a716-446655440000",
# "userID": "446655440000-e29b-41d4-a716-550e8400",
# "createdAt": 1616161616,
# "status": "created",
# "uploadURL": "...",
# "config": { ... }
# }
# Upload PDF file to the presigned URL
curl -X PUT 'PRESIGNED_UPLOAD_URL' \
-H 'Content-Type: application/pdf' \
-H "Content-Length: PDF_FILE_SIZE" \
--data-binary "@path/to/your/file.pdf"
# Check job status
curl 'https://api.lookscanned.io/v1/scan-jobs/JOB_ID' \
-H "Authorization: Bearer ${LOOKSCANNED_API_TOKEN}"
# Response will include status and downloadURL when completed
# {
# "jobID": "550e8400-e29b-41d4-a716-446655440000",
# "status": "completed",
# "downloadURL": "...",
# ...
# }
# Download the PDF
curl -o scanned.pdf 'DOWNLOAD_URL'API Bearer Token
El token pertenece a tu cuenta y puedes regenerarlo cuando quieras. El escaneo por API requiere una cuenta Pro: sin un token válido la API responde 401, y sin el rol Pro responde 403.
El escaneo por API es una función Pro
Esta cuenta aún no tiene Pro; al mejorarla, el token aparece aquí. Sin token, o sin el rol, la API responde 401 / 403.
Pruébalo
Ajusta los parámetros, mira cómo cambia el cuerpo de la petición y lanza las tres llamadas a la API.
Parámetros de escaneo
La prueba llama a la API con tu token y necesita una cuenta Pro; los parámetros y el cuerpo de la petición se pueden consultar libremente.
{
"config": {
"rotate": 1,
"rotate_var": 0.5,
"colorspace": "gray",
"blur": 0,
"noise": 0,
"border": false,
"brightness": 1.3,
"contrast": 1.3,
"resolution": 150,
"output_format": "image/jpeg"
}
}Información del trabajo de escaneo
ejemplo{
"jobID": "3f9c1e64-0000-4000-8000-00000000a71b",
"userID": "8f21c4b0-0000-4000-8000-000000004a17",
"createdAt": 1724409600,
"status": "completed",
"inputUploadedAt": 1724409601,
"completedAt": 1724409602,
"numPages": 6,
"downloadURL": "https://…/output/3f9c.pdf?X-Amz-…"
}Referencia de la API
| Método | Ruta | Descripción |
|---|---|---|
| POST | /v1/scan-jobs | Crea un trabajo de escaneo. Envía config y, opcionalmente, webhookUrl; recibes el objeto del trabajo con status created y una uploadURL prefirmada. |
| PUT | {uploadURL}La dirección S3 prefirmada del paso anterior, que no está en api.lookscanned.io | Sube el PDF de origen con Content-Type: application/pdf y Content-Length. La dirección lleva su propia firma, así que no añadas la cabecera Authorization. |
| GET | /v1/scan-jobs/{jobID} | Consulta un único trabajo para hacer sondeo. Con created incluye uploadURL; con completed, downloadURL. |
| GET | /v1/scan-jobs | Lista tus propios trabajos, filtrados por jobID, status o createdAfter. |
401sin token válido403la cuenta no es Pro404el trabajo no existe
Cuerpo de la petición
| Campo | Tipo | Predeterminado | Descripción |
|---|---|---|---|
| webhookUrl | string · — | — | Se llama una vez cuando el trabajo termina, así no hace falta ir consultando el estado. |
| config.colorspace | 'gray' | 'sRGB' · gray | gray | Espacio de color de la imagen de salida; gray es un escaneo en blanco y negro. |
| config.resolution | number · 72 | 72 | Resolución de la imagen de salida, en PPP. |
| config.rotate | number · — | — | Rotación de todo el documento, en grados. |
| config.rotate_var | number · — | — | Margen de la rotación aleatoria por página, en grados: el aspecto del papel colocado torcido. |
| config.blur | number · 0 | 0 | Cantidad de desenfoque. |
| config.noise | number · 0 | 0 | Cantidad de ruido. |
| config.brightness | number · 1 | 1 | Brillo; 1 lo deja sin cambios. |
| config.contrast | number · 1 | 1 | Contraste; 1 lo deja sin cambios. |
| config.border | boolean · false | false | Si se añade un borde de escaneo a la página. |
| config.output_format | 'image/png' | 'image/jpeg' · image/jpeg | image/jpeg | Formato de imagen al que se renderizan las páginas. |
Todos los campos son opcionales. Los valores de partida de «Pruébalo» (resolución 150, rotación 1, brillo y contraste 1,3) son la combinación que recomienda la aplicación web, no los valores predeterminados de la API.
Campos del objeto del trabajo que conviene vigilar
- status
- created / processing / completed / failed: determina si aparecen las dos direcciones siguientes.
- uploadURL
- Solo mientras está created. Dirección de subida prefirmada que caduca.
- downloadURL
- Solo cuando está completed. Dirección de descarga prefirmada que caduca.
- inputUploadedAt / completedAt
- Cuándo terminó de subirse el origen y cuándo terminó el trabajo; la diferencia es el tiempo de procesamiento.
Preguntas frecuentes
¿El escaneo por API necesita Pro?
Sí. Sin un token válido la API responde 401, y una cuenta sin el rol Pro recibe 403. Si mejoras la cuenta e inicias sesión, el token aparece en esta página.
¿Cómo sé cuándo ha terminado un trabajo?
De dos formas: consulta GET /v1/scan-jobs/{jobID} periódicamente, o pasa un webhookUrl al crear el trabajo y deja que el servicio te avise una vez.
¿El resultado es igual que al escanear en la web?
Sí. Ambos usan la misma implementación del efecto de escaneo, y el espacio de color, la resolución, la rotación, el desenfoque, el ruido, el brillo, el contraste y el borde de config son las mismas opciones de la web con otro nombre: con los mismos parámetros se obtiene el mismo resultado. Solo cambia dónde ocurre el trabajo: en local en la página, en remoto a través de la API.
¿Puedo guardar las direcciones de subida y descarga para reutilizarlas?
Es mejor no hacerlo. uploadURL y downloadURL son direcciones prefirmadas con caducidad; cuando expiran hay que volver a consultar el trabajo para obtener unas nuevas.
¿Cuánto tarda un trabajo?
Depende del número de páginas y de la resolución. Unas pocas páginas suelen terminar en segundos, y una resolución mayor o un documento más largo tardan más. La diferencia entre inputUploadedAt y completedAt es el tiempo real transcurrido.
¿Qué pasa si un trabajo falla?
El estado pasa a failed. Las causas habituales son un archivo que no es un PDF válido, restricciones de cifrado o una subida interrumpida. Comprueba que el archivo se abre y crea un trabajo nuevo.
¿Puedo consultar trabajos anteriores?
Sí. GET /v1/scan-jobs lista tus propios trabajos y admite filtros por jobID, status y createdAfter, suficiente para cuadrar cuentas o repetir una descarga.