المسح الضوئي عبر 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.
أين يفيد
إنتاج مجمّع من الخادم
تمر العقود والفواتير والتقارير المنشأة على الخادم مباشرة عبر تأثير المسح الضوئي، دون أن يعيد أحد العمل يدويًا على صفحة الويب.
داخل نظام قائم
أضف إلى نظام إدارة العملاء أو تخطيط الموارد أو التذاكر إجراءً باسم «تصدير نسخة ممسوحة ضوئيًا» يستدعي الواجهة.
سلاسل الأتمتة
تبدأ منصات مثل 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
الرمز مرتبط بحسابك ويمكن توليده من جديد في أي وقت. ويتطلب المسح الضوئي عبر API حساب Pro: فبدون رمز صالح تردّ الواجهة بـ 401، وبدون دور Pro تردّ بـ 403.
المسح الضوئي عبر API ميزة من ميزات Pro
هذا الحساب ليس على Pro بعد؛ وبعد الترقية يظهر الرمز هنا. وبدون رمز أو بدون الدور تردّ الواجهة بـ 401 / 403.
جرّبه
اضبط المعاملات وراقب جسم الطلب وهو يتغيّر معها، ثم نفّذ الاستدعاءات الثلاثة على الواجهة.
معاملات المسح الضوئي
تشغيل التجربة يستدعي الواجهة برمزك ويتطلب حساب 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-…"
}مرجع الواجهة
| الطريقة | المسار | الوصف |
|---|---|---|
| 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 | دقة الصورة الناتجة، بوحدة نقطة لكل بوصة. |
| 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 اشتراك Pro؟
نعم. فبدون رمز صالح تردّ الواجهة بـ 401، والحساب الذي لا يحمل دور Pro يتلقى 403. وبعد الترقية وتسجيل الدخول يظهر الرمز في هذه الصفحة.
كيف أعرف أن المهمة انتهت؟
بطريقتين: الاستعلام الدوري عن GET /v1/scan-jobs/{jobID}، أو تمرير webhookUrl عند إنشاء المهمة لتتصل بك الخدمة مرة واحدة عند الانتهاء.
هل النتيجة نفسها نتيجة المسح على صفحة الويب؟
هي نفسها. فالجانبان يستخدمان التنفيذ ذاته لتأثير المسح الضوئي، وفضاء الألوان والدقة والدوران والضبابية والضوضاء والسطوع والتباين والحدود في config تقابل خيارات صفحة الويب بالأسماء نفسها: المعاملات نفسها تعطي المخرجات نفسها. ولا يختلف سوى مكان المعالجة — محليًا في الصفحة، وعن بُعد عبر الواجهة.
هل أستطيع حفظ عنواني الرفع والتنزيل وإعادة استخدامهما؟
يُفضّل ألا تفعل. فـ uploadURL وdownloadURL عنوانان موقّعان مسبقًا ولهما مدة صلاحية؛ وبعد انتهائها عليك قراءة المهمة من جديد للحصول على عنوانين جديدين.
كم تستغرق المهمة؟
يتوقف ذلك على عدد الصفحات والدقة. فالمستند من بضع صفحات ينتهي عادة خلال ثوانٍ، وكلما ارتفعت الدقة وزادت الصفحات طال الوقت. والفرق بين inputUploadedAt وcompletedAt يعطي الزمن الفعلي.
ماذا أفعل إذا فشلت المهمة؟
تتحول الحالة إلى failed. والأسباب المعتادة أن الملف ليس PDF صالحًا، أو أن التشفير يقيّده، أو أن الرفع انقطع. تأكد من أن الملف يُفتح، ثم أنشئ مهمة جديدة.
هل يمكنني الاطلاع على المهام السابقة؟
نعم. يسرد GET /v1/scan-jobs مهامك أنت ويقبل الترشيح حسب jobID وstatus وcreatedAfter، وهو ما يكفي للمطابقة أو لإعادة التنزيل.