Apis
By the end of this lesson you'll be able to call any external API from PHP — fetch data with file_get_contents and cURL, send authenticated POST requests, parse the JSON you get back, and handle errors, status codes, and timeouts like a professional.
Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.
Part of the free Php course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
1️⃣ The Quick Way: file_get_contents
The fastest way to read a remote URL is file_get_contents() — the same function you'd use to read a file, except you hand it a web address. It downloads the response body as one big string. Most APIs answer in JSON (a text format for structured data), so you then call json_decode($json, true) to turn that text into a PHP associative array you can read by key. The true matters: it gives you an array, not a harder-to-use object.
Two lines do the real work: one fetches the text, one decodes it. The catch is that file_get_contents on a URL is bare-bones — it sends no useful headers, has no obvious timeout, and on failure returns false with only a warning. For real APIs you need to configure the request, which is the next section.
2️⃣ Adding Headers & a Timeout: Stream Context
A stream context is an options bundle you pass as the third argument to file_get_contents . It lets you set the HTTP method, request headers (many APIs reject a request with no User-Agent ), and a timeout so a dead server can't hang your script forever. After the call, PHP auto-fills $http_response_header with the response status line so you can see what the server actually said.
Setting ignore_errors => true is important: without it, a 404 or 500 makes file_get_contents return false and throw away the body — but error responses usually contain a helpful JSON message you'll want to read. This works, but cURL gives you cleaner control, so it's what professionals reach for.
3️⃣ The Standard Way: cURL
cURL is PHP's built-in library for HTTP requests and the tool you'll use most. Every cURL request follows the same four steps: curl_init() creates a request handle, curl_setopt() configures it (one option at a time), curl_exec() sends it, and curl_close() frees the handle. The single most important option is CURLOPT_RETURNTRANSFER => true — without it, cURL prints the response instead of returning it as a string.
Notice curl_getinfo($ch, CURLINFO_HTTP_CODE) — that's how you read the HTTP status code (200 = OK, 404 = not found, 500 = server error). Always set a CURLOPT_TIMEOUT so one slow API can't freeze your whole app.
4️⃣ POST, JSON Bodies & Bearer Auth
To send data — create a record, log in, trigger an action — you make a POST request. Set CURLOPT_POST => true , put your JSON in CURLOPT_POSTFIELDS , and add a Content-Type: application/json header so the server knows how to read it. Most APIs require authentication : the common pattern is a Bearer token , a secret string you send in an Authorization: Bearer <token> header. Read that token from the environment with getenv() — never write it into your code.
This is the production shape of an API call: send the request, then branch on the result. First check whether the request failed at the network level ( $body === false ), then check the status code for 2xx success, and only decode the JSON when you know you have a real response. Skipping these checks is the number-one source of API bugs.
5️⃣ Status Codes, Timeouts & Rate-Limit Etiquette
An API can refuse you for many reasons, and it tells you which with a status code : 200/201 success, 401 bad token, 404 not found, 429 too many requests, 500 server error. Always branch on the code before trusting the body. Equally, always set a timeout so a hanging server can't take your app down with it.
Being a good API citizen — rate-limit etiquette — keeps you from getting blocked. Don't request the same data twice; cache it. Don't hammer an endpoint in a tight loop; add a small delay. And if you get a 429 , read the Retry-After header and wait that many seconds before retrying. Now you try the two core skills — fetching and guarding.
Fill in each ___ using the 👉 hint, then run it and check it against the Output panel.
One more. A real client never decodes a response it hasn't checked. Add the condition that only proceeds on a successful status code.
📋 Quick Reference — Consuming APIs
No code is filled in this time — just a brief and an outline. Write it yourself, run it on a machine with internet, then check your result against the expected output in the comments. This is exactly the fetch-check-decode loop you'll use against every real API.
Practice quiz
What does json_decode($json, true) return?
- A stdClass object
- A JSON string
- An associative array
- A cURL handle
Answer: An associative array. Passing true as the second argument gives an associative array you can read by key, instead of a harder-to-use object.
What are the four steps of every cURL request, in order?
- init, setopt, exec, close
- open, read, write, close
- connect, send, receive, free
- create, configure, send, parse
Answer: init, setopt, exec, close. Every cURL request follows curl_init -> curl_setopt -> curl_exec -> curl_close.
What does CURLOPT_RETURNTRANSFER => true do?
- Sends the request body
- Sets a timeout
- Enables POST
- Returns the response as a string instead of printing it
Answer: Returns the response as a string instead of printing it. Without CURLOPT_RETURNTRANSFER, cURL prints the response; with it, curl_exec returns the body as a string.
How do you read the HTTP status code after a cURL request?
- curl_status($ch)
- curl_getinfo($ch, CURLINFO_HTTP_CODE)
- curl_code($ch)
- $ch->status
Answer: curl_getinfo($ch, CURLINFO_HTTP_CODE). curl_getinfo($ch, CURLINFO_HTTP_CODE) reads the status code (200, 404, 500, etc).
Which cURL option carries the JSON body of a POST request?
- CURLOPT_POSTFIELDS
- CURLOPT_BODY
- CURLOPT_HTTPHEADER
- CURLOPT_DATA
Answer: CURLOPT_POSTFIELDS. Set CURLOPT_POST => true and put the encoded JSON in CURLOPT_POSTFIELDS, with a Content-Type: application/json header.
Why does json_decode() sometimes return null?
- The body was too large
- cURL was not installed
- The input was not valid JSON (e.g. an HTML error page or empty body)
- The URL used https
Answer: The input was not valid JSON (e.g. an HTML error page or empty body). json_decode returns null on invalid JSON. Check the status code first, then json_last_error() to see what went wrong.
Where should API keys and tokens be stored?
- Hard-coded in the source file
- In environment variables, read with getenv()
- In a public config.js
- Committed to git for the team
Answer: In environment variables, read with getenv(). Never hard-code or commit secrets. Read them from the environment with getenv() and keep them in a .gitignored .env file.
How should you send a Bearer token for authentication?
- As a URL query parameter
- In CURLOPT_POSTFIELDS
- As a cookie only
- In an Authorization: Bearer <token> request header
Answer: In an Authorization: Bearer <token> request header. The common pattern is an Authorization: Bearer <token> header, with the token read from the environment.
What should you do when an API responds with HTTP 429?
- Immediately retry in a tight loop
- Read the Retry-After header and wait that many seconds before retrying
- Switch to file_get_contents
- Ignore it and continue
Answer: Read the Retry-After header and wait that many seconds before retrying. 429 means too many requests. Respect rate limits: read Retry-After and back off before trying again; cache to avoid re-requesting.
Why must you always set CURLOPT_TIMEOUT?
- It speeds up the request
- It enables HTTPS
- Without it, cURL waits indefinitely for a dead server and can freeze your app
- It validates the JSON
Answer: Without it, cURL waits indefinitely for a dead server and can freeze your app. With no timeout, cURL waits forever for a slow or dead server. Always set CURLOPT_TIMEOUT so a slow API fails fast.