An API is the official, well-defined way one piece of software lets another talk to it. Think of a restaurant menu: you don't walk into the kitchen or need to know how the food is cooked; you order from the menu and the dish arrives. The menu tells you what you can ask for and in what form, and an API does the same for programs. When a mobile app shows your order history, or an online store asks the payment provider whether a payment went through, an API call is happening behind the scenes.
In day-to-day programming, the most common kind of API today is a REST API: your program sends an HTTP request, just like the one a browser sends to load a page, to a specific URL and usually gets JSON back. Below we look at what these requests are made of, what the responses mean, and how to call an API from PHP and Python.
Short answer: An API (application programming interface) is a contract that defines what data and functionality one piece of software exposes to another, and in what format. A REST API is the most common kind on the web: a program sends an HTTP request with a method such as GET or POST to a specific URL and gets back a status code and, usually, a JSON body.
Where APIs fit in programming
The term API (Application Programming Interface) is broad. The functions a library exposes to you are that library's API, too. But in everyday web work, when someone says "the X API" they almost always mean a web API: a set of URLs on the internet you can send requests to.
A few familiar examples:
- A store's mobile app and its web dashboard both pull data from the same API, so the core logic is written only once.
- Your website calls an SMS provider's API to send verification codes.
- An accounting system reads new invoices from the store's API every night.
The key point is that both sides agree only on the contract: which URL, what input, what output. Either side can change its language, database or architecture behind that contract without the other noticing.
What is a REST API?
REST is a style for designing APIs on top of HTTP. The core idea is simple: everything is a resource, and every resource has its own URL. What you want to do with that resource is expressed by the HTTP method.
GET /v1/orders list orders
GET /v1/orders/1042 get one order
POST /v1/orders create a new order
PATCH /v1/orders/1042 update part of an order
DELETE /v1/orders/1042 delete or cancel an order
Notice there are no verbs in the URLs (no /getOrder or /deleteOrder). The method is the verb; the URL only says which resource you're talking about. Another important property of REST is that every request is stateless: the server doesn't need to remember anything from a previous request, and everything it needs, including who is sending it, comes in the request itself.
HTTP methods
| Method | Used for | Has a body? | Safe to repeat? |
|---|---|---|---|
| GET | Reading | No | Yes |
| POST | Creating a resource or triggering an action | Yes | No |
| PUT | Replacing a resource entirely | Yes | Yes |
| PATCH | Updating part of a resource | Yes | Depends on implementation |
| DELETE | Deleting | Usually not | Yes |
The last column is what idempotent means: if a PUT or DELETE arrives twice because of a network glitch, the result is the same as if it arrived once. Two POSTs, however, mean two orders. That's why serious APIs accept an idempotency key on sensitive operations such as payments, so they can detect a duplicate request.
Status codes
Every HTTP response carries a three-digit number that tells you what happened before you even read the body. The first digit is the category: 2xx success, 4xx a problem with the request, 5xx a problem on the server.
| Code | Meaning |
|---|---|
| 200 OK | Success; the result is in the body |
| 201 Created | A new resource was created |
| 204 No Content | Success, with no body (e.g. after a delete) |
| 400 Bad Request | The request is malformed |
| 401 Unauthorized | You're not authenticated, or the token is invalid |
| 403 Forbidden | You're authenticated but not allowed to do this |
| 404 Not Found | No such resource |
| 422 Unprocessable Content | The data was parsed but isn't valid (e.g. a bad email address) |
| 429 Too Many Requests | You've exceeded the rate limit |
| 500 Internal Server Error | Something went wrong on the server |
A practical rule for code that calls an API: don't retry a 4xx; fix the request first. A 5xx or 429 can be retried after a short pause.
JSON: the common language
Request and response bodies in most REST APIs are JSON: a readable text format that almost every language can parse and produce.
{
"id": 1042,
"status": "paid",
"total": 18500,
"currency": "EUR",
"items": [
{ "product_id": 7, "title": "Book", "quantity": 2 }
],
"created_at": "2026-10-09T08:30:00Z"
}
A real request with curl
curl is the simplest way to see an API up close. The examples use the placeholder host api.example.com and assume the token is in the SHOP_API_TOKEN environment variable:
curl -i https://api.example.com/v1/orders/1042 \
-H "Accept: application/json" \
-H "Authorization: Bearer $SHOP_API_TOKEN"
The -i flag shows the response headers as well:
HTTP/2 200
content-type: application/json
{"id":1042,"status":"paid","total":18500,"currency":"EUR"}
Creating a new order with 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}'
A successful response is usually 201 Created, with the new resource in the body and often a Location header pointing to its URL.
Authentication: how does an API know who you are?
Most APIs aren't public; they need to know who is making the request. Three common approaches, from simplest to most involved:
- API key: a fixed string you get from the service's dashboard and send with every request, usually in a header. Simple, and well suited to server-to-server communication.
- Bearer token: a token issued after login or from a dashboard and sent in the
Authorization: Bearer ...header. It typically has an expiry and a defined scope. - OAuth 2.0: for when a user needs to let your app access another service on their behalf without giving you their password. The "Sign in with..." buttons belong to this family.
A few rules apply whichever method you use:
- Don't hard-code keys or tokens or commit them to git; keep them in environment variables or a
.envfile that isn't tracked. - HTTPS only. A token sent over plain HTTP is effectively public.
- Never put a key in browser-side code or a mobile app; anyone can extract it. The app should call your own API, and your server should talk to the third-party service with the key.
- Issue a separate key per consumer, so if one leaks you revoke only that one.
What makes a good API?
If you're building an API yourself, these are what separate a pleasant API from a painful one:
- Consistency: the same naming style throughout (all
snake_caseor allcamelCase), plural resource names (/orders), one date format (ISO 8601). - Correct status codes: returning errors as 200 with
"success": falsein the body makes life hard for everyone calling your API. - Clear errors: the message should say which field failed and why, not just "error".
- Versioning:
/v1/in the path, so that when the contract changes, existing consumers don't break. - Pagination: never return a whole list in one go; use
pageorcursorwith a maximum page size. - Rate limiting, advertised in response headers, with a 429 status when exceeded.
- Documentation with examples: one working curl example beats pages of prose.
If an endpoint does heavy work (generating a report, bulk sending), it's better to accept the request, hand the job to a queue and immediately return 202 Accepted. That pattern is covered in Laravel queues for slow work.
Calling an API from PHP
Using PHP's built-in cURL extension, with no extra libraries (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;
In Laravel, the framework's HTTP client makes this shorter. throw() raises an exception on any 4xx or 5xx response:
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();
Calling an API from Python
The requests library is the usual choice (pip install requests):
import os
import requests
headers = {
"Accept": "application/json",
"Authorization": f"Bearer {os.environ['SHOP_API_TOKEN']}",
}
# Fetch one order
resp = requests.get(
"https://api.example.com/v1/orders/1042",
headers=headers,
timeout=10,
)
resp.raise_for_status()
order = resp.json()
print(order["status"])
# Create a new order; json= encodes the body and sets 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"])
In both languages, don't forget two things: a timeout, because without one a slow service can hang your program indefinitely, and checking the status code before using the response. If you're torn between the two for your backend, see PHP or Python for backend development.
Frequently asked questions
What is the difference between an API and a web service?
A web service is an API that's available over a network, usually via HTTP. API is the broader term and also covers the functions a library exposes to a program, so every web service is an API, but not every API is a web service.
What is the difference between GET and POST in an API?
GET reads data, has no body and is safe to repeat. POST creates a resource or triggers an action, sends its data in the body, and can take effect twice if it arrives twice, for example creating two orders.
What is an API key and where should I store it?
An API key is a fixed string you get from a service's dashboard and send with each request, usually in a header, so the service knows who is calling. Keep it in an environment variable or a .env file outside git, send it only over HTTPS, and never put it in browser-side code or a mobile app.
What is the difference between a 401 and a 403 error?
401 means you aren't authenticated or your token is invalid. 403 means the server knows who you are but you don't have permission to do that.
How do I test an API?
The simplest way is the curl command-line tool: give it the URL, the headers you need such as Authorization, and a JSON body if required, and use the -i flag to see the status code and response headers. Once that works, send the same request from PHP or Python.
Wrap-up
An API is a contract that says what one piece of software exposes to another and in what form. A REST API implements that contract over HTTP: resources have URLs, GET, POST, PATCH and DELETE say what to do, the status code reports the outcome, and the data is usually JSON. To get started, try an API with curl, then call it from PHP or Python, and always set a timeout, check the status code and keep your keys out of your code.