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 篩選,用來對帳或補下載。