Сканирование через API
Сканирование через API превращает PDF в правдоподобную отсканированную копию одним REST-вызовом — это удобно для автоматических процессов и интеграций. Создайте задание, загрузите PDF, затем опрашивайте статус или дождитесь webhook: три шага из любой среды и любого языка, способного отправить HTTP-запрос. Цветовое пространство, разрешение, поворот, размытие, шум, яркость, контраст и рамка настраиваются.
Как проходит вызов
Создать задание
POST /v1/scan-jobs
Отправьте свой config и, при желании, webhookUrl — в ответ придут jobID и предподписанный uploadURL.
Загрузить PDF
PUT {uploadURL}
Отправьте файл методом PUT прямо на предподписанный адрес S3 из предыдущего шага — токен не нужен.
Забрать отсканированную копию
GET /v1/scan-jobs/{jobID}
Опрашивайте статус или дождитесь webhook; как только задание станет completed, скачайте результат по downloadURL.
Где это пригодится
Пакетный выпуск на бэкенде
Договоры, счета и отчёты, созданные на сервере, сразу проходят через эффект сканирования, и никому не нужно повторять то же самое вручную на веб-странице.
Внутри существующей системы
Добавьте в CRM, ERP или систему заявок действие «выгрузить отсканированную копию» и пусть оно обращается к API.
Автоматические конвейеры
CI, n8n, Zapier и подобные платформы запускают задание по событию, а webhook по завершении передаёт работу дальше.
Большие очереди файлов
Задания асинхронные: после создания каждое обрабатывается само по себе, а ход работы виден через status и createdAfter.
Языки и среды
API — это обычные HTTP и JSON, поэтому обратиться к нему может любой язык или платформа автоматизации, умеющая отправлять запросы.
Примеры кода
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
Токен привязан к вашей учётной записи, и его можно в любой момент создать заново. Сканирование через API требует учётной записи Pro: без действительного токена API отвечает 401, без роли Pro — 403.
Сканирование через API — функция Pro
У этой учётной записи ещё нет Pro; после перехода токен появится здесь. Без токена или без роли API отвечает 401 / 403.
Попробовать
Настройте параметры, посмотрите, как меняется тело запроса, и выполните три вызова к API.
Параметры сканирования
Пробный запуск обращается к API с вашим токеном и требует учётной записи Pro; параметры и тело запроса можно смотреть свободно.
{
"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"
}
}Информация о задании сканирования
пример{
"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
| Метод | Путь | Описание |
|---|---|---|
| POST | /v1/scan-jobs | Создаёт задание на сканирование. Отправьте config и, при необходимости, webhookUrl; в ответ придёт объект задания со статусом created и предподписанный uploadURL. |
| PUT | {uploadURL}Предподписанный адрес S3 из предыдущего шага, он находится не на api.lookscanned.io | Загружает исходный PDF с заголовками Content-Type: application/pdf и Content-Length. Адрес уже содержит подпись, поэтому заголовок Authorization добавлять не нужно. |
| GET | /v1/scan-jobs/{jobID} | Читает одно задание — для опроса статуса. При created содержит uploadURL, при completed — downloadURL. |
| GET | /v1/scan-jobs | Перечисляет ваши задания с фильтрами по jobID, status и createdAfter. |
401нет действительного токена403учётная запись не Pro404задание не найдено
Тело запроса
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
| webhookUrl | string · — | — | Вызывается один раз по завершении задания, так что опрашивать статус не нужно. |
| config.colorspace | 'gray' | 'sRGB' · gray | gray | Цветовое пространство итогового изображения; gray — чёрно-белая отсканированная копия. |
| config.resolution | number · 72 | 72 | Разрешение итогового изображения, в DPI. |
| config.rotate | number · — | — | Поворот всего документа, в градусах. |
| config.rotate_var | number · — | — | Диапазон случайного поворота каждой страницы, в градусах — эффект криво положенного листа. |
| config.blur | number · 0 | 0 | Сила размытия. |
| config.noise | number · 0 | 0 | Сила шума. |
| config.brightness | number · 1 | 1 | Яркость; 1 оставляет её без изменений. |
| config.contrast | number · 1 | 1 | Контраст; 1 оставляет его без изменений. |
| config.border | boolean · false | false | Добавлять ли странице рамку сканирования. |
| config.output_format | 'image/png' | 'image/jpeg' · image/jpeg | image/jpeg | Формат изображения, в который отрисовываются страницы. |
Любое поле можно не указывать. Значения, с которых начинает раздел «Попробовать» — разрешение 150, поворот 1, яркость и контраст 1,3 — это сочетание, рекомендованное веб-приложением, а не значения API по умолчанию.
Поля объекта задания, за которыми стоит следить
- status
- created / processing / completed / failed — определяет, присутствуют ли два адреса ниже.
- uploadURL
- Только пока задание в статусе created. Предподписанный адрес загрузки с ограниченным сроком действия.
- downloadURL
- Только после статуса completed. Предподписанный адрес скачивания с ограниченным сроком действия.
- inputUploadedAt / completedAt
- Когда завершилась загрузка исходника и когда завершилось задание; разница даёт время обработки.
Частые вопросы
Нужен ли Pro для сканирования через API?
Да. Без действительного токена API отвечает 401, а учётная запись без роли Pro получает 403. После перехода на Pro и входа токен будет на этой странице.
Как узнать, что задание завершено?
Двумя способами: опрашивать GET /v1/scan-jobs/{jobID} или передать webhookUrl при создании задания и позволить сервису один раз вызвать вас в ответ.
Результат такой же, как при сканировании на веб-странице?
Такой же. Обе стороны используют одну и ту же реализацию эффекта сканирования, а цветовое пространство, разрешение, поворот, размытие, шум, яркость, контраст и рамка в config соответствуют одноимённым настройкам на веб-странице: одинаковые параметры дают одинаковый результат. Различается только место обработки — локально на странице и удалённо через API.
Можно ли сохранить адреса загрузки и скачивания и использовать их повторно?
Лучше не стоит. uploadURL и downloadURL — предподписанные адреса с ограниченным сроком действия; после истечения нужно снова запросить задание и получить новые.
Сколько обрабатывается задание?
Зависит от числа страниц и разрешения. Несколько страниц обычно готовы за секунды, а более высокое разрешение и длинные документы занимают больше времени. Разница между inputUploadedAt и completedAt даёт фактическое время.
Что делать, если задание не выполнилось?
Статус станет failed. Обычные причины — файл не является корректным PDF, ограничения шифрования или прерванная загрузка. Убедитесь, что файл открывается, и создайте новое задание.
Можно ли посмотреть прошлые задания?
Да. GET /v1/scan-jobs перечисляет ваши задания и поддерживает фильтры по jobID, status и createdAfter — этого достаточно для сверки или повторного скачивания.