Numérisation par API
La numérisation par API transforme un PDF en copie numérisée réaliste via un appel REST, ce qui convient aux traitements automatisés et aux intégrations applicatives. Créez la tâche, importez le PDF, puis interrogez le statut ou attendez le webhook : trois étapes, depuis n'importe quel environnement ou langage capable d'envoyer une requête HTTP. L'espace colorimétrique, la résolution, la rotation, le flou, le bruit, la luminosité, le contraste et la bordure se règlent librement.
Déroulé d'un appel
Créer la tâche
POST /v1/scan-jobs
Envoyez votre config et, si vous le souhaitez, un webhookUrl ; vous recevez un jobID et une uploadURL présignée.
Importer le PDF
PUT {uploadURL}
Faites un PUT du fichier directement vers l'adresse S3 présignée de l'étape précédente : aucun jeton n'est nécessaire.
Récupérer la copie numérisée
GET /v1/scan-jobs/{jobID}
Interrogez le statut ou attendez le webhook ; une fois la tâche completed, téléchargez-la depuis downloadURL.
Cas d'usage
Production par lot côté serveur
Les contrats, factures et rapports générés sur le serveur passent directement par l'effet de numérisation, sans que personne refasse l'opération à la main sur la page web.
Dans un système existant
Ajoutez une action « exporter une copie numérisée » à un CRM, un ERP ou un outil de tickets, et laissez-le appeler l'API.
Chaînes d'automatisation
CI, n8n, Zapier et consorts lancent une tâche sur un événement, et le webhook enchaîne sur l'étape suivante une fois terminé.
Files de fichiers volumineuses
Les tâches sont asynchrones : créez-les et chacune est traitée de son côté, l'avancement restant consultable via status et createdAfter.
Langages et environnements
L'API repose sur HTTP et JSON standard : tout langage ou toute plateforme d'automatisation capable d'envoyer une requête peut l'appeler.
Exemples de code
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
Le jeton est rattaché à votre compte et peut être régénéré à tout moment. La numérisation par API exige un compte Pro : sans jeton valide l'API répond 401, et sans le rôle Pro elle répond 403.
La numérisation par API est une fonction Pro
Ce compte n’a pas encore Pro ; une fois l’offre souscrite, le jeton s’affiche ici. Sans jeton, ou sans le rôle, l’API répond 401 / 403.
Essayer
Réglez les paramètres, regardez le corps de la requête suivre, puis lancez les trois appels vers l’API.
Paramètres de numérisation
L’essai appelle l’API avec votre jeton et exige un compte Pro ; les paramètres et le corps de la requête restent librement consultables.
{
"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"
}
}Informations sur la tâche de numérisation
exemple{
"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-…"
}Référence de l'API
| Méthode | Chemin | Description |
|---|---|---|
| POST | /v1/scan-jobs | Crée une tâche de numérisation. Envoyez config et, si besoin, webhookUrl ; vous recevez l'objet de la tâche avec le statut created et une uploadURL présignée. |
| PUT | {uploadURL}L'adresse S3 présignée de l'étape précédente, qui ne se trouve pas sur api.lookscanned.io | Envoie le PDF source avec Content-Type: application/pdf et Content-Length. L'adresse porte sa propre signature : n'ajoutez pas d'en-tête Authorization. |
| GET | /v1/scan-jobs/{jobID} | Lit une seule tâche, pour l'interrogation périodique. En created, elle contient uploadURL ; en completed, downloadURL. |
| GET | /v1/scan-jobs | Liste vos propres tâches, filtrées par jobID, status ou createdAfter. |
401aucun jeton valide403le compte n'est pas Pro404tâche introuvable
Corps de la requête
| Champ | Type | Par défaut | Description |
|---|---|---|---|
| webhookUrl | string · — | — | Appelé une fois la tâche terminée, ce qui évite d’avoir à interroger le statut. |
| config.colorspace | 'gray' | 'sRGB' · gray | gray | Espace colorimétrique de l'image produite ; gray correspond à une numérisation en noir et blanc. |
| config.resolution | number · 72 | 72 | Résolution de l'image produite, en PPP. |
| config.rotate | number · — | — | Rotation de tout le document, en degrés. |
| config.rotate_var | number · — | — | Amplitude de la rotation aléatoire page par page, en degrés : l'aspect d'une feuille posée de travers. |
| config.blur | number · 0 | 0 | Intensité du flou. |
| config.noise | number · 0 | 0 | Intensité du bruit. |
| config.brightness | number · 1 | 1 | Luminosité ; 1 ne change rien. |
| config.contrast | number · 1 | 1 | Contraste ; 1 ne change rien. |
| config.border | boolean · false | false | Ajouter ou non une bordure de numérisation à la page. |
| config.output_format | 'image/png' | 'image/jpeg' · image/jpeg | image/jpeg | Format d'image dans lequel les pages sont rendues. |
Tous les champs sont facultatifs. Les valeurs de départ d'« Essayer » (résolution 150, rotation 1, luminosité et contraste 1,3) sont la combinaison recommandée par l'application web, pas les valeurs par défaut de l'API.
Champs à surveiller dans l'objet de la tâche
- status
- created / processing / completed / failed : détermine la présence des deux adresses ci-dessous.
- uploadURL
- Uniquement tant que la tâche est created. Adresse d'import présignée, à durée limitée.
- downloadURL
- Uniquement une fois la tâche completed. Adresse de téléchargement présignée, à durée limitée.
- inputUploadedAt / completedAt
- Fin de l'import du fichier source et fin de la tâche ; l'écart donne la durée de traitement.
Questions fréquentes
La numérisation par API nécessite-t-elle Pro ?
Oui. Sans jeton valide l'API répond 401, et un compte sans le rôle Pro reçoit 403. Une fois l'offre Pro souscrite et la session ouverte, le jeton s'affiche sur cette page.
Comment savoir qu'une tâche est terminée ?
De deux manières : interroger GET /v1/scan-jobs/{jobID}, ou transmettre un webhookUrl à la création de la tâche et laisser le service vous rappeler une fois.
Le résultat est-il identique à celui de la page web ?
Oui. Les deux utilisent la même implémentation de l'effet de numérisation, et l'espace colorimétrique, la résolution, la rotation, le flou, le bruit, la luminosité, le contraste et la bordure de config correspondent aux options de même nom sur la page web : à paramètres identiques, résultat identique. Seul l'endroit du traitement diffère — en local sur la page, à distance via l'API.
Puis-je stocker les adresses d'import et de téléchargement pour les réutiliser ?
Mieux vaut éviter. uploadURL et downloadURL sont des adresses présignées à durée limitée ; une fois expirées, il faut relire la tâche pour en obtenir de nouvelles.
Combien de temps prend une tâche ?
Cela dépend du nombre de pages et de la résolution. Quelques pages se terminent généralement en quelques secondes ; une résolution plus élevée ou un document plus long prennent plus de temps. L'écart entre inputUploadedAt et completedAt donne la durée réelle.
Que faire si une tâche échoue ?
Le statut passe à failed. Les causes habituelles sont un fichier qui n'est pas un PDF valide, des restrictions de chiffrement ou un import interrompu. Vérifiez que le fichier s'ouvre, puis créez une nouvelle tâche.
Puis-je consulter les tâches passées ?
Oui. GET /v1/scan-jobs liste vos propres tâches et accepte des filtres sur jobID, status et createdAfter, ce qui suffit pour un rapprochement ou un nouveau téléchargement.