Scansione via API
La scansione via API trasforma un PDF in una copia scansionata realistica con una chiamata REST, adatta a processi automatizzati e integrazioni applicative. Crea il lavoro, carica il PDF e interroga lo stato o attendi il webhook: tre passaggi, da qualsiasi ambiente o linguaggio in grado di inviare una richiesta HTTP. Spazio colore, risoluzione, rotazione, sfocatura, rumore, luminosità, contrasto e bordo sono tutti configurabili.
Come funziona una chiamata
Creare il lavoro
POST /v1/scan-jobs
Invia la tua config e, se vuoi, un webhookUrl; ricevi un jobID e una uploadURL prefirmata.
Caricare il PDF
PUT {uploadURL}
Esegui il PUT del file direttamente all'indirizzo S3 prefirmato del passaggio precedente: nessun token necessario.
Ritirare la copia scansionata
GET /v1/scan-jobs/{jobID}
Interroga lo stato o attendi il webhook; quando il lavoro è completed, scaricala da downloadURL.
Dove si inserisce
Produzione in blocco dal backend
Contratti, fatture e report generati sul server passano direttamente per l'effetto scansione, senza che nessuno ripeta l'operazione a mano sulla pagina web.
Dentro un sistema esistente
Aggiungi a un CRM, un ERP o un sistema di ticket un'azione «esporta copia scansionata» che chiama l'API.
Catene di automazione
CI, n8n, Zapier e simili avviano un lavoro su un evento, e al termine il webhook passa la mano al passaggio successivo.
Code di file consistenti
I lavori sono asincroni: una volta creati vengono elaborati ciascuno per conto proprio, con l'avanzamento consultabile tramite status e createdAfter.
Linguaggi e ambienti
L'API è HTTP e JSON standard: qualsiasi linguaggio o piattaforma di automazione in grado di inviare una richiesta può usarla.
Esempi di codice
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
Il token appartiene al tuo account e puoi rigenerarlo in qualsiasi momento. La scansione via API richiede un account Pro: senza un token valido l'API risponde 401, senza il ruolo Pro risponde 403.
La scansione via API è una funzione Pro
Questo account non ha ancora Pro; dopo l’upgrade il token compare qui. Senza token, o senza il ruolo, l’API risponde 401 / 403.
Provalo
Regola i parametri, guarda il corpo della richiesta cambiare di conseguenza, poi esegui le tre chiamate verso l’API.
Parametri di scansione
Una prova chiama l’API con il tuo token e richiede un account Pro; i parametri e il corpo della richiesta restano liberamente consultabili.
{
"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"
}
}Informazioni lavoro di scansione
esempio{
"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-…"
}Riferimento API
| Metodo | Percorso | Descrizione |
|---|---|---|
| POST | /v1/scan-jobs | Crea un lavoro di scansione. Invia config e, se serve, webhookUrl; ricevi l'oggetto del lavoro con stato created e una uploadURL prefirmata. |
| PUT | {uploadURL}L'indirizzo S3 prefirmato del passaggio precedente, non su api.lookscanned.io | Carica il PDF di origine con Content-Type: application/pdf e Content-Length. L'indirizzo porta con sé la propria firma: non aggiungere l'intestazione Authorization. |
| GET | /v1/scan-jobs/{jobID} | Legge un singolo lavoro, per l'interrogazione periodica. Con created contiene uploadURL, con completed downloadURL. |
| GET | /v1/scan-jobs | Elenca i tuoi lavori, filtrati per jobID, status o createdAfter. |
401nessun token valido403l'account non è Pro404lavoro inesistente
Corpo della richiesta
| Campo | Tipo | Predefinito | Descrizione |
|---|---|---|---|
| webhookUrl | string · — | — | Viene chiamato una volta quando il lavoro finisce, così non serve interrogare lo stato. |
| config.colorspace | 'gray' | 'sRGB' · gray | gray | Spazio colore dell'immagine prodotta; gray è una scansione in bianco e nero. |
| config.resolution | number · 72 | 72 | Risoluzione dell'immagine prodotta, in DPI. |
| config.rotate | number · — | — | Rotazione dell'intero documento, in gradi. |
| config.rotate_var | number · — | — | Ampiezza della rotazione casuale pagina per pagina, in gradi: l'aspetto di un foglio appoggiato storto. |
| config.blur | number · 0 | 0 | Intensità della sfocatura. |
| config.noise | number · 0 | 0 | Intensità del rumore. |
| config.brightness | number · 1 | 1 | Luminosità; 1 la lascia invariata. |
| config.contrast | number · 1 | 1 | Contrasto; 1 lo lascia invariato. |
| config.border | boolean · false | false | Se aggiungere alla pagina un bordo di scansione. |
| config.output_format | 'image/png' | 'image/jpeg' · image/jpeg | image/jpeg | Formato immagine in cui vengono renderizzate le pagine. |
Tutti i campi sono facoltativi. I valori di partenza di «Provalo» (risoluzione 150, rotazione 1, luminosità e contrasto 1,3) sono la combinazione consigliata dall'applicazione web, non i valori predefiniti dell'API.
Campi da tenere d'occhio nell'oggetto del lavoro
- status
- created / processing / completed / failed: determina se i due indirizzi qui sotto sono presenti.
- uploadURL
- Solo finché è created. Indirizzo di caricamento prefirmato, con scadenza.
- downloadURL
- Solo quando è completed. Indirizzo di download prefirmato, con scadenza.
- inputUploadedAt / completedAt
- Quando è terminato il caricamento dell'originale e quando è finito il lavoro; la differenza è il tempo di elaborazione.
Domande frequenti
La scansione via API richiede Pro?
Sì. Senza un token valido l'API risponde 401 e un account privo del ruolo Pro riceve 403. Dopo l'upgrade e l'accesso, il token compare in questa pagina.
Come faccio a sapere quando un lavoro è finito?
In due modi: interrogare GET /v1/scan-jobs/{jobID}, oppure passare un webhookUrl alla creazione del lavoro e lasciare che il servizio richiami una volta.
Il risultato è uguale a quello della scansione sulla pagina web?
Sì. Entrambi usano la stessa implementazione dell'effetto scansione, e spazio colore, risoluzione, rotazione, sfocatura, rumore, luminosità, contrasto e bordo in config sono le opzioni omonime della pagina web: a parità di parametri il risultato è lo stesso. Cambia solo dove avviene l'elaborazione, in locale sulla pagina e da remoto tramite l'API.
Posso salvare gli indirizzi di caricamento e download e riutilizzarli?
Meglio di no. uploadURL e downloadURL sono indirizzi prefirmati a tempo; una volta scaduti bisogna rileggere il lavoro per ottenerne di nuovi.
Quanto dura un lavoro?
Dipende dal numero di pagine e dalla risoluzione. Poche pagine si completano di solito in pochi secondi; risoluzioni più alte o documenti più lunghi richiedono più tempo. La differenza tra inputUploadedAt e completedAt è il tempo effettivamente trascorso.
Cosa faccio se un lavoro fallisce?
Lo stato diventa failed. Le cause abituali sono un file che non è un PDF valido, restrizioni di cifratura o un caricamento interrotto. Verifica che il file si apra e crea un nuovo lavoro.
Posso consultare i lavori passati?
Sì. GET /v1/scan-jobs elenca i tuoi lavori e accetta filtri su jobID, status e createdAfter: abbastanza per una riconciliazione o per riscaricare un file.