API 扫描
API 扫描通过接口调用把 PDF 变成逼真的扫描件,适合自动化流程和应用集成。创建任务、上传 PDF、轮询或等回调三步即可接入,色彩空间、分辨率、旋转、模糊、噪点、亮度、对比度和边框都可自定义,任何能发 HTTP 请求的环境或语言都能调用。
调用流程
创建任务
POST /v1/scan-jobs
带上 config 与可选的 webhookUrl,拿到 jobID 和预签名 uploadURL。
上传 PDF
PUT {uploadURL}
把文件直接 PUT 到上一步返回的 S3 预签名地址,无需再带 Token。
取回扫描件
GET /v1/scan-jobs/{jobID}
轮询状态或等 webhook 回调,completed 后用 downloadURL 下载。
适合这些场景
后端批量出件
合同、发票、报表在服务端生成后直接过一遍扫描,不用人工再走一次网页。
接进现有系统
CRM、ERP、工单系统里加一个「导出扫描件」,走接口调用即可。
自动化流水线
CI 或 n8n、Zapier 这类平台按事件触发,完成后用 webhook 通知下一步。
大量文件排队
任务式接口,创建后各自异步处理,可按 status、createdAfter 查询进度。
支持的语言与环境
接口是标准的 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
Token 与账号绑定,可随时重新生成。API 扫描需要专业版账号:没带有效 Token 时接口返回 401,账号没有专业版角色时返回 403。
API 扫描是专业版功能
当前账号还没有专业版;升级后即可在此处获取 Token。没有 Token 或角色时,接口会返回 401 / 403。
试一试
调整参数,请求体会跟着一起变,然后依次调用三个接口。
扫描参数
试跑会用你的 Token 真实调用接口,需要专业版账号;参数和请求体可以随便看。
{
"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-…"
}接口参考
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /v1/scan-jobs | 创建扫描任务。带上 config 与可选的 webhookUrl,返回 jobID、status 为 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未带有效 Token403账号不是专业版404任务不存在
请求体参数
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
| 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)是网页端的推荐组合,不是接口默认值。
任务对象里要留意的字段
- status
- created / processing / completed / failed,决定下面两个地址是否出现。
- uploadURL
- 仅 created 时给出,预签名上传地址,有时效。
- downloadURL
- 仅 completed 时给出,预签名下载地址,有时效。
- inputUploadedAt / completedAt
- 源文件上传完成、任务完成的时间戳,可用来算耗时。
常见问题
API 扫描需要专业版吗?
需要。没带有效 Token 时接口返回 401,账号没有专业版角色时返回 403;升级并登录后即可在本页获取 Token。
怎么知道任务什么时候完成?
两种方式:轮询 GET /v1/scan-jobs/{jobID},或在创建任务时传 webhookUrl,由服务在完成后回调一次。
结果和网页端的扫描一样吗?
一样。两边用的是同一套扫描效果实现,config 里的色彩空间、分辨率、旋转、模糊、噪点、亮度、对比度和边框对应网页端同名选项,相同参数得到一致的输出;区别只在处理位置——网页端在本地,API 在远程服务。
上传和下载地址能存起来复用吗?
不建议。uploadURL 与 downloadURL 都是有时效的预签名地址,过期后需要再查一次任务重新获取。
任务处理要多久?
取决于页数和分辨率。多数几页的文档在数秒内完成;分辨率越高、页数越多耗时越长。可以用 inputUploadedAt 和 completedAt 的差值统计实际耗时。
任务失败了怎么办?
状态会变成 failed。常见原因是上传的文件不是有效 PDF、加密受限或上传中断。确认文件可正常打开后重新创建一个任务即可。
可以查历史任务吗?
可以。GET /v1/scan-jobs 会列出你自己的任务,支持按 jobID、status、createdAfter 过滤,用来做对账或补下载。