API-Scan
Der API-Scan macht aus einem PDF über einen REST-Aufruf eine realistisch gescannte Kopie – passend für automatisierte Abläufe und App-Integrationen. Auftrag anlegen, PDF hochladen, Status abfragen oder auf den Webhook warten: drei Schritte, aus jeder Umgebung und jeder Sprache, die eine HTTP-Anfrage senden kann. Farbraum, Auflösung, Drehung, Weichzeichnung, Rauschen, Helligkeit, Kontrast und Rand sind frei einstellbar.
Ablauf eines Aufrufs
Auftrag anlegen
POST /v1/scan-jobs
Senden Sie Ihre config und optional eine webhookUrl; zurück kommen eine jobID und eine vorsignierte uploadURL.
PDF hochladen
PUT {uploadURL}
Die Datei per PUT direkt an die vorsignierte S3-Adresse aus dem vorigen Schritt schicken – ohne Token.
Gescannte Kopie abholen
GET /v1/scan-jobs/{jobID}
Status abfragen oder auf den Webhook warten; sobald der Auftrag completed ist, über downloadURL herunterladen.
Wofür es sich eignet
Stapelausgabe im Backend
Verträge, Rechnungen und Berichte, die auf dem Server entstehen, laufen direkt durch den Scan-Effekt, ohne dass jemand den Vorgang auf der Webseite von Hand wiederholt.
In einem bestehenden System
Ergänzen Sie CRM, ERP oder Ticketsystem um eine Aktion „gescannte Kopie exportieren“, die die API aufruft.
Automatisierte Abläufe
CI, n8n, Zapier und Ähnliches starten einen Auftrag bei einem Ereignis, und der Webhook übergibt nach Abschluss an den nächsten Schritt.
Große Dateiwarteschlangen
Aufträge sind asynchron: einmal angelegt, wird jeder für sich verarbeitet, und der Fortschritt bleibt über status und createdAfter abrufbar.
Sprachen und Umgebungen
Die API ist gewöhnliches HTTP und JSON – jede Sprache und jede Automatisierungsplattform, die eine Anfrage senden kann, kann sie aufrufen.
Codebeispiele
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
Das Token gehört zu Ihrem Konto und lässt sich jederzeit neu erzeugen. Der API-Scan setzt ein Pro-Konto voraus: ohne gültiges Token antwortet die API mit 401, ohne die Pro-Rolle mit 403.
Der API-Scan ist eine Pro-Funktion
Dieses Konto hat noch kein Pro; nach dem Upgrade steht das Token hier. Ohne Token oder ohne die Rolle antwortet die API mit 401 / 403.
Ausprobieren
Stellen Sie die Parameter ein, sehen Sie dem Anfragerumpf beim Mitwachsen zu und schicken Sie dann die drei Aufrufe an die API.
Scan-Parameter
Ein Testlauf ruft die API mit Ihrem Token auf und braucht ein Pro-Konto; Parameter und Anfragerumpf können Sie frei ansehen.
{
"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"
}
}Scan-Auftragsinformationen
Beispiel{
"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-…"
}API-Referenz
| Methode | Pfad | Beschreibung |
|---|---|---|
| POST | /v1/scan-jobs | Legt einen Scan-Auftrag an. Senden Sie config und optional webhookUrl; zurück kommt das Auftragsobjekt mit dem Status created und einer vorsignierten uploadURL. |
| PUT | {uploadURL}Die vorsignierte S3-Adresse aus dem vorigen Schritt, nicht auf api.lookscanned.io | Lädt das Quell-PDF mit Content-Type: application/pdf und Content-Length hoch. Die Adresse trägt ihre eigene Signatur – fügen Sie keinen Authorization-Header hinzu. |
| GET | /v1/scan-jobs/{jobID} | Liest einen einzelnen Auftrag, zum Abfragen des Fortschritts. Bei created enthält er uploadURL, bei completed downloadURL. |
| GET | /v1/scan-jobs | Listet Ihre eigenen Aufträge auf, gefiltert nach jobID, status oder createdAfter. |
401kein gültiges Token403das Konto ist nicht Pro404Auftrag nicht vorhanden
Anfragerumpf
| Feld | Typ | Standard | Beschreibung |
|---|---|---|---|
| webhookUrl | string · — | — | Wird einmal aufgerufen, sobald der Auftrag fertig ist – dann müssen Sie nicht abfragen. |
| config.colorspace | 'gray' | 'sRGB' · gray | gray | Farbraum des Ausgabebilds; gray ist ein Schwarzweiß-Scan. |
| config.resolution | number · 72 | 72 | Auflösung des Ausgabebilds in DPI. |
| config.rotate | number · — | — | Drehung des gesamten Dokuments in Grad. |
| config.rotate_var | number · — | — | Spanne der zufälligen Drehung je Seite in Grad – der Eindruck von schief aufgelegtem Papier. |
| config.blur | number · 0 | 0 | Stärke der Weichzeichnung. |
| config.noise | number · 0 | 0 | Stärke des Rauschens. |
| config.brightness | number · 1 | 1 | Helligkeit; 1 lässt sie unverändert. |
| config.contrast | number · 1 | 1 | Kontrast; 1 lässt ihn unverändert. |
| config.border | boolean · false | false | Ob die Seite einen Scan-Rand erhält. |
| config.output_format | 'image/png' | 'image/jpeg' · image/jpeg | image/jpeg | Bildformat, in das die Seiten gerendert werden. |
Alle Felder sind optional. Die Startwerte unter „Ausprobieren“ – Auflösung 150, Drehung 1, Helligkeit und Kontrast 1,3 – sind die von der Web-App empfohlene Kombination, nicht die Standardwerte der API.
Felder im Auftragsobjekt, auf die es ankommt
- status
- created / processing / completed / failed – entscheidet, ob die beiden folgenden Adressen vorhanden sind.
- uploadURL
- Nur solange created. Eine vorsignierte Upload-Adresse mit begrenzter Gültigkeit.
- downloadURL
- Erst wenn completed. Eine vorsignierte Download-Adresse mit begrenzter Gültigkeit.
- inputUploadedAt / completedAt
- Wann der Upload der Quelle fertig war und wann der Auftrag fertig war; die Differenz ist die Verarbeitungsdauer.
Häufige Fragen
Braucht der API-Scan Pro?
Ja. Ohne gültiges Token antwortet die API mit 401, und ein Konto ohne die Pro-Rolle bekommt 403. Nach dem Upgrade und der Anmeldung steht das Token auf dieser Seite.
Woran erkenne ich, dass ein Auftrag fertig ist?
Auf zwei Wegen: GET /v1/scan-jobs/{jobID} abfragen, oder beim Anlegen eine webhookUrl mitgeben und den Dienst einmal zurückrufen lassen.
Ist das Ergebnis dasselbe wie beim Scannen auf der Webseite?
Ja. Beide nutzen dieselbe Umsetzung des Scan-Effekts, und Farbraum, Auflösung, Drehung, Weichzeichnung, Rauschen, Helligkeit, Kontrast und Rand in config sind die gleichnamigen Optionen der Webseite: gleiche Parameter, gleiches Ergebnis. Unterschiedlich ist nur der Ort der Verarbeitung – lokal auf der Seite, entfernt über die API.
Kann ich die Upload- und Download-Adressen speichern und wiederverwenden?
Besser nicht. uploadURL und downloadURL sind vorsignierte Adressen mit begrenzter Gültigkeit; nach Ablauf müssen Sie den Auftrag erneut lesen, um neue zu erhalten.
Wie lange dauert ein Auftrag?
Das hängt von Seitenzahl und Auflösung ab. Wenige Seiten sind meist in Sekunden fertig; höhere Auflösungen und längere Dokumente dauern länger. Die Differenz zwischen inputUploadedAt und completedAt ist die tatsächliche Dauer.
Was tun, wenn ein Auftrag fehlschlägt?
Der Status wechselt auf failed. Übliche Ursachen sind eine Datei, die kein gültiges PDF ist, Verschlüsselungsbeschränkungen oder ein abgebrochener Upload. Prüfen Sie, ob sich die Datei öffnen lässt, und legen Sie einen neuen Auftrag an.
Kann ich frühere Aufträge nachschlagen?
Ja. GET /v1/scan-jobs listet Ihre eigenen Aufträge und lässt sich nach jobID, status und createdAfter filtern – genug für einen Abgleich oder einen erneuten Download.