API یعنی راه رسمی و تعریفشدهای که یک نرمافزار برای حرفزدن با نرمافزار دیگر در اختیار میگذارد. مثل منوی رستوران: شما به آشپزخانه نمیروید و نمیدانید غذا چطور پخته میشود؛ فقط از روی منو سفارش میدهید و غذا تحویل میگیرید. منو میگوید چه چیزهایی را میشود خواست و به چه شکلی؛ API هم همین را برای برنامهها مشخص میکند. وقتی اپلیکیشن موبایل فهرست سفارشهایتان را نشان میدهد یا فروشگاه اینترنتی وضعیت پرداخت را از درگاه بانک میپرسد، در پشت صحنه یک API صدا زده شده است.
در برنامهنویسی، رایجترین نوع API امروز REST API است: برنامهی شما یک درخواست HTTP، مثل همانی که مرورگر برای باز کردن صفحه میفرستد، به یک آدرس مشخص میفرستد و جواب را معمولاً به شکل JSON پس میگیرد. در ادامه میبینیم این درخواستها از چه اجزایی ساخته میشوند، جوابها چه معنایی دارند و چطور از PHP و Python یک API را صدا بزنیم.
API در برنامهنویسی دقیقاً کجاست؟
کلمهی API (Application Programming Interface) معنای گستردهای دارد. توابعی که یک کتابخانه در اختیارتان میگذارد هم API آن کتابخانه است. اما وقتی در کار روزمرهی وب کسی میگوید «API فلان سرویس»، تقریباً همیشه منظورش یک Web API است: مجموعهای از آدرسها روی اینترنت که میشود به آنها درخواست فرستاد.
چند مثال آشنا:
- اپلیکیشن موبایل و پنل وب یک فروشگاه هر دو از یک API داده میگیرند؛ منطق اصلی فقط یکجا نوشته میشود.
- سایت شما برای ارسال پیامک تأیید، API سرویس پیامک را صدا میزند.
- سیستم حسابداری هر شب فاکتورهای تازه را از API فروشگاه میخواند.
نکتهی کلیدی این است که دو طرف فقط سر قرارداد توافق دارند: چه آدرسی، با چه ورودی، چه خروجیای. هر طرف میتواند پشت این قرارداد زبان، پایگاهداده یا معماریاش را عوض کند و طرف دیگر چیزی نفهمد.
REST API چیست؟
REST سبکی برای طراحی API روی HTTP است. ایدهی اصلیاش ساده است: همهچیز منبع (resource) است و هر منبع آدرس خودش را دارد. کاری که میخواهید روی آن منبع انجام دهید را با متد HTTP میگویید.
GET /v1/orders فهرست سفارشها
GET /v1/orders/1042 یک سفارش مشخص
POST /v1/orders ساختن سفارش تازه
PATCH /v1/orders/1042 تغییر بخشی از یک سفارش
DELETE /v1/orders/1042 حذف یا لغو سفارش
دقت کنید که در آدرسها فعل نیست (/getOrder یا /deleteOrder نداریم). فعل همان متد است و آدرس فقط میگوید دربارهی کدام منبع حرف میزنیم. ویژگی مهم دیگر REST این است که هر درخواست مستقل است: سرور لازم نیست چیزی از درخواست قبلی به خاطر بسپارد و همهی اطلاعات لازم، از جمله هویت فرستنده، در خود درخواست میآید.
متدهای HTTP
| متد | کاربرد | بدنه دارد؟ | تکرارش بیخطر است؟ |
|---|---|---|---|
| GET | خواندن | نه | بله |
| POST | ساختن منبع تازه یا اجرای یک عمل | بله | نه |
| PUT | جایگزینی کامل یک منبع | بله | بله |
| PATCH | تغییر بخشی از یک منبع | بله | بسته به پیادهسازی |
| DELETE | حذف | معمولاً نه | بله |
ستون آخر همان مفهوم idempotent است: اگر یک درخواست PUT یا DELETE به خاطر قطعی شبکه دو بار برسد، نتیجه با یک بار فرقی ندارد. اما دو POST یعنی دو سفارش. به همین دلیل APIهای جدی برای عملیات حساسی مثل پرداخت، کلیدی به نام idempotency key در درخواست میگیرند تا درخواست تکراری را تشخیص دهند.
کدهای وضعیت
هر جواب HTTP یک عدد سهرقمی دارد که قبل از خواندن بدنه میگوید چه اتفاقی افتاد. رقم اول دسته را مشخص میکند: 2xx موفق، 4xx ایراد از درخواستکننده، 5xx ایراد از سرور.
| کد | معنی |
|---|---|
| 200 OK | موفق؛ جواب در بدنه است |
| 201 Created | منبع تازه ساخته شد |
| 204 No Content | موفق، بدون بدنه (مثلاً بعد از حذف) |
| 400 Bad Request | درخواست بدشکل است |
| 401 Unauthorized | هویت شما مشخص نیست یا توکن نامعتبر است |
| 403 Forbidden | هویتتان معلوم است اما اجازه ندارید |
| 404 Not Found | چنین منبعی نیست |
| 422 Unprocessable Content | دادهها خوانده شد اما معتبر نیست (مثلاً ایمیل غلط) |
| 429 Too Many Requests | بیش از حد مجاز درخواست فرستادهاید |
| 500 Internal Server Error | خطایی در سرور رخ داده |
قاعدهی عملی برای کدی که API صدا میزند: 4xx را دوباره نفرستید، اول درخواست را درست کنید. 5xx و 429 را میشود با کمی مکث دوباره امتحان کرد.
JSON: زبان مشترک
بدنهی درخواستها و جوابها در بیشتر REST APIها JSON است: قالبی متنی و خوانا که تقریباً همهی زبانها خواندن و نوشتنش را بلدند.
{
"id": 1042,
"status": "paid",
"total": 1850000,
"currency": "IRR",
"items": [
{ "product_id": 7, "title": "کتاب", "quantity": 2 }
],
"created_at": "2026-10-09T08:30:00Z"
}
یک درخواست واقعی با curl
curl سادهترین راه برای دیدن یک API از نزدیک است. در مثالها آدرس فرضی api.example.com را به کار بردهایم و فرض کردهایم توکن در متغیر محیطی SHOP_API_TOKEN است:
curl -i https://api.example.com/v1/orders/1042 \
-H "Accept: application/json" \
-H "Authorization: Bearer $SHOP_API_TOKEN"
گزینهی -i سرآیندهای جواب را هم نشان میدهد:
HTTP/2 200
content-type: application/json
{"id":1042,"status":"paid","total":1850000,"currency":"IRR"}
ساختن یک سفارش تازه با POST:
curl -i -X POST https://api.example.com/v1/orders \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $SHOP_API_TOKEN" \
-d '{"product_id": 7, "quantity": 2}'
جواب موفق معمولاً 201 Created است، همراه با منبع ساختهشده در بدنه و اغلب یک سرآیند Location که آدرس آن منبع را میگوید.
احراز هویت: API از کجا میفهمد شما کی هستید؟
بیشتر APIها عمومی نیستند و باید بدانند چه کسی درخواست میفرستد. سه روش رایج، از ساده به پیچیده:
- API key: یک رشتهی ثابت که از پنل سرویس میگیرید و در هر درخواست، معمولاً در یک سرآیند، میفرستید. ساده است و برای ارتباط سرور با سرور مناسب.
- Bearer token: توکنی که بعد از ورود یا از پنل صادر میشود و در سرآیند
Authorization: Bearer ...میآید. معمولاً تاریخ انقضا و سطح دسترسی مشخص دارد. - OAuth 2.0: وقتی کاربر باید به برنامهی شما اجازه بدهد از طرفش به سرویس دیگری دسترسی داشته باشد، بدون اینکه رمزش را به شما بدهد. همان دکمههای «ورود با...» از این خانوادهاند.
چند قاعده که در هر روشی برقرار است:
- کلید و توکن را در کد ننویسید و در git نگذارید؛ در متغیر محیطی یا فایل
.env(که در git نیست) نگهش دارید. - فقط روی HTTPS. توکنی که روی HTTP ساده برود، عملاً منتشر شده است.
- کلید را در کد سمت مرورگر یا اپلیکیشن موبایل قرار ندهید؛ هر کسی میتواند بیرونش بکشد. اپلیکیشن باید API خودتان را صدا بزند و سرور شما با کلید سرویس بیرونی حرف بزند.
- برای هر مصرفکننده کلید جدا بسازید تا اگر یکی لو رفت، فقط همان را باطل کنید.
یک API خوب چه ویژگیهایی دارد؟
اگر خودتان API میسازید، اینها فرق یک API قابلاستفاده با یک API دردسرساز است:
- یکدستی: نامگذاری یکسان (همه
snake_caseیا همهcamelCase)، آدرسهای جمع (/orders)، قالب تاریخ یکسان (ISO 8601). - کد وضعیت درست: خطا با کد 200 و
"success": falseدر بدنه، کار هر کسی را که API را صدا میزند سخت میکند. - خطای قابلفهم: پیام خطا بگوید کدام فیلد و چرا، نه فقط «خطا».
- نسخهبندی:
/v1/در آدرس، تا روزی که قرارداد عوض شد مصرفکنندههای قدیمی نشکنند. - صفحهبندی: هیچ فهرستی را یکجا برنگردانید؛ با
pageیاcursorو سقف تعداد. - محدودیت نرخ و اعلامش در سرآیندها، همراه با کد 429.
- مستندات با مثال: یک نمونهی curl که کار کند از چند صفحه توضیح مفیدتر است.
اگر API کاری سنگین انجام میدهد (ساخت گزارش، ارسال انبوه)، بهتر است درخواست را بپذیرد، کار را به صف بسپارد و فوراً 202 Accepted برگرداند؛ این الگو در صفها در لاراول توضیح داده شده است.
فراخوانی API از PHP
با افزونهی cURL خود PHP، بدون هیچ کتابخانهی اضافه (PHP 8.3 به بالا):
<?php
$ch = curl_init('https://api.example.com/v1/orders/1042');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . getenv('SHOP_API_TOKEN'),
],
]);
$body = curl_exec($ch);
if ($body === false) {
throw new RuntimeException('Request failed: ' . curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status !== 200) {
throw new RuntimeException("Unexpected status {$status}");
}
$order = json_decode($body, true, flags: JSON_THROW_ON_ERROR);
echo $order['status'], PHP_EOL;
در لاراول همین کار با کلاینت HTTP خود فریمورک کوتاهتر میشود. throw() برای هر جواب 4xx یا 5xx استثنا میاندازد:
use Illuminate\Support\Facades\Http;
$order = Http::withToken(config('services.shop.token'))
->acceptJson()
->timeout(10)
->get('https://api.example.com/v1/orders/1042')
->throw()
->json();
فراخوانی API از Python
کتابخانهی requests رایجترین انتخاب است (pip install requests):
import os
import requests
headers = {
"Accept": "application/json",
"Authorization": f"Bearer {os.environ['SHOP_API_TOKEN']}",
}
# خواندن یک سفارش
resp = requests.get(
"https://api.example.com/v1/orders/1042",
headers=headers,
timeout=10,
)
resp.raise_for_status()
order = resp.json()
print(order["status"])
# ساختن سفارش تازه؛ json= بدنه را JSON میکند و Content-Type را هم میگذارد
resp = requests.post(
"https://api.example.com/v1/orders",
headers=headers,
json={"product_id": 7, "quantity": 2},
timeout=10,
)
resp.raise_for_status()
print(resp.status_code, resp.json()["id"])
در هر دو زبان دو چیز را فراموش نکنید: timeout، چون بدون آن یک سرویس کند میتواند برنامهی شما را بینهایت معطل کند، و بررسی کد وضعیت پیش از استفاده از جواب. اگر بین این دو زبان برای بکاند مردد هستید، PHP یا Python برای بکاند را ببینید.
جمعبندی
API قراردادی است که میگوید یک نرمافزار چه چیزهایی را و به چه شکلی در اختیار نرمافزار دیگر میگذارد. REST API این قرارداد را روی HTTP پیاده میکند: منبعها آدرس دارند، متدهای GET و POST و PATCH و DELETE میگویند چه کاری انجام شود، کد وضعیت نتیجه را اعلام میکند و دادهها معمولاً JSON است. برای شروع، یک API را با curl امتحان کنید، بعد از PHP یا Python صدایش بزنید و همیشه timeout بگذارید، کد وضعیت را بررسی کنید و کلیدها را بیرون از کد نگه دارید.