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 می‌گویید.

text
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 است: قالبی متنی و خوانا که تقریباً همه‌ی زبان‌ها خواندن و نوشتنش را بلدند.

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 است:

bash
curl -i https://api.example.com/v1/orders/1042 \
  -H "Accept: application/json" \
  -H "Authorization: Bearer $SHOP_API_TOKEN"

گزینه‌ی -i سرآیندهای جواب را هم نشان می‌دهد:

http
HTTP/2 200
content-type: application/json

{"id":1042,"status":"paid","total":1850000,"currency":"IRR"}

ساختن یک سفارش تازه با POST:

bash
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
<?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 استثنا می‌اندازد:

php
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):

python
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 بگذارید، کد وضعیت را بررسی کنید و کلیدها را بیرون از کد نگه دارید.