Working with APIs
Create PHP JSON APIs and retrieve database records through focused endpoints.
- Creating a Simple JSON API with PHP
- Reading API Data with JavaScript
- Creating a Database-Driven API with PHP and MySQL
- Creating API Endpoints for Specific Data
Creating a Simple JSON API with PHP
In this tutorial, you will create a simple API using PHP.
The API will return JSON data when visited in a browser.
This is the same type of data format used by many real-world APIs.
Project Structure
Create a folder called:
api-demo
Inside the folder place:
api-demo
├── api.php
└── smart_security_data.json
Create the API File
Create:
api.php
Add:
<?php
header(
"Content-Type: application/json"
);
echo file_get_contents(
"smart_security_data.json"
);
?>
Save the file.
Understanding the Code
This line tells the browser that JSON data is being returned:
header(
"Content-Type: application/json"
);
This line reads the JSON file:
file_get_contents(
"smart_security_data.json"
);
This line sends the JSON data to the browser:
echo file_get_contents(
"smart_security_data.json"
);
Test the API
Open:
http://localhost/api-demo/api.php
You should see:
{
"homeId": "GC-HOME-014",
"location": "Gold Coast"
}
along with the rest of the JSON data.
Test the API in a New Browser Tab
Notice that the URL now behaves like a data source.
Instead of displaying a webpage, it returns structured JSON data.
This is the same concept used by many modern APIs.
Example:
https://example.com/api/users
https://example.com/api/products
https://example.com/api/weather
Complete API File
<?php
header(
"Content-Type: application/json"
);
echo file_get_contents(
"smart_security_data.json"
);
?>
You have successfully created a JSON API using PHP.
Reading API Data with JavaScript
The API returns JSON data when visited in a browser.
In this tutorial, you will use JavaScript to read data from the API and display it on a webpage.
Project Structure
Your project should contain:
api-demo
├── api.php
├── index.html
└── smart_security_data.json
Create the HTML Page
Create:
index.html
Add:
<!DOCTYPE html>
<html>
<head>
<title>API Demo</title>
</head>
<body>
<h1>API Dashboard</h1>
<div id="output"></div>
<script>
</script>
</body>
</html>
Save the file.
Read the API
Inside the <script> tags add:
fetch("api.php")
.then(response => response.json())
.then(data => {
console.log(data);
});
Save the file.
Test the API Request
Open:
http://localhost/api-demo/
Press:
F12
Open the Console tab.
You should see the JSON object displayed.
Display the Home Information
Replace:
console.log(data);
with:
document.getElementById(
"output"
).innerHTML = `
<p>
<strong>Home ID:</strong>
${data.homeId}
</p>
<p>
<strong>Location:</strong>
${data.location}
</p>
`;
Refresh the page.
You should see:
Home ID: GC-HOME-014
Location: Gold Coast
Display Device Information
Replace the existing code with:
let html = `
<h2>Devices</h2>
<table border="1">
<tr>
<th>Device ID</th>
<th>Device Type</th>
<th>Room</th>
</tr>
`;
data.devices.forEach(device => {
html += `
<tr>
<td>${device.deviceId}</td>
<td>${device.deviceType}</td>
<td>${device.room}</td>
</tr>
`;
});
html += "</table>";
document.getElementById(
"output"
).innerHTML = html;
Refresh the page.
You should now see a table of devices.
Why Use an API?
Previously, the data was loaded directly from:
fetch("smart_security_data.json")
Now the data is loaded from:
fetch("api.php")
This means the data can be:
- Generated dynamically
- Stored in a database
- Updated automatically
- Filtered before being sent
The webpage does not need to know where the data comes from. It only needs the JSON returned by the API.
Complete Page
<!DOCTYPE html>
<html>
<head>
<title>API Demo</title>
</head>
<body>
<h1>API Dashboard</h1>
<div id="output"></div>
<script>
fetch("api.php")
.then(response => response.json())
.then(data => {
let html = `
<h2>Devices</h2>
<table border="1">
<tr>
<th>Device ID</th>
<th>Device Type</th>
<th>Room</th>
</tr>
`;
data.devices.forEach(device => {
html += `
<tr>
<td>${device.deviceId}</td>
<td>${device.deviceType}</td>
<td>${device.room}</td>
</tr>
`;
});
html += "</table>";
document.getElementById(
"output"
).innerHTML = html;
});
</script>
</body>
</html>
You have successfully consumed data from an API using JavaScript.
Creating a Database-Driven API with PHP and MySQL
This tutorial creates a read-only JSON endpoint using PHP, PDO and fictional database records.
An API should return only the data needed for its stated purpose. Do not expose password hashes, session data, personal information or unnecessary database fields.
Create the endpoint
Create api/events.php:
<?php
declare(strict_types=1);
require __DIR__ . '/../includes/database.php';
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');
try {
$statement = $pdo->query(
'SELECT
event_id,
event_timestamp,
device_id,
room,
event_type,
severity,
access_result
FROM security_events
ORDER BY event_timestamp DESC
LIMIT 100'
);
$events = $statement->fetchAll();
echo json_encode(
[
'ok' => true,
'count' => count($events),
'events' => $events
],
JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES
);
} catch (Throwable $error) {
http_response_code(500);
echo json_encode([
'ok' => false,
'error' => 'The fictional event data is unavailable.'
]);
}
The endpoint:
Test the response
Open the endpoint through XAMPP:
http://localhost/project/api/events.php
A successful response should have this shape:
{
"ok": true,
"count": 2,
"events": [
{
"event_id": 14,
"event_timestamp": "2026-06-14 20:57:06",
"device_id": "CAM-01",
"room": "Science Lab",
"event_type": "failed_login",
"severity": "medium",
"access_result": "denied"
}
]
}
The values are fictional. Your live response will reflect the current database.
Important boundary
This example is a public, read-only classroom endpoint. A production API may also require authentication, authorisation, pagination, rate limiting, audit logging and a documented privacy purpose.
Check
- The endpoint returns valid JSON.
- The response uses an appropriate HTTP status when processing fails.
- No credentials, password hashes or unnecessary fields are exposed.
- The response is limited to a reasonable number of records.
- All records are fictional.
Creating API Endpoints for Specific Data
Focused endpoints return the smallest useful result for a particular user need. This tutorial adds filtered and summary endpoints using PDO and fictional records.
Endpoint for one severity
Create api/events-by-severity.php:
<?php
declare(strict_types=1);
require __DIR__ . '/../includes/database.php';
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');
$allowed = ['low', 'medium', 'high', 'critical'];
$severity = strtolower(trim($_GET['severity'] ?? ''));
if (!in_array($severity, $allowed, true)) {
http_response_code(400);
echo json_encode([
'ok' => false,
'error' => 'Choose low, medium, high or critical.'
]);
exit;
}
$statement = $pdo->prepare(
'SELECT
event_id,
event_timestamp,
device_id,
room,
event_type,
severity
FROM security_events
WHERE severity = :severity
ORDER BY event_timestamp DESC
LIMIT 100'
);
$statement->execute(['severity' => $severity]);
$events = $statement->fetchAll();
echo json_encode([
'ok' => true,
'filter' => ['severity' => $severity],
'count' => count($events),
'events' => $events
]);
Test it with a fictional category:
http://localhost/project/api/events-by-severity.php?severity=high
The allow-list rejects unexpected values and the prepared statement keeps the supplied value separate from the SQL.
Endpoint for summary totals
Create api/event-summary.php:
<?php
declare(strict_types=1);
require __DIR__ . '/../includes/database.php';
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');
$statement = $pdo->query(
'SELECT severity, COUNT(*) AS total
FROM security_events
GROUP BY severity
ORDER BY severity'
);
$rows = $statement->fetchAll();
$summary = [];
foreach ($rows as $row) {
$summary[] = [
'severity' => $row['severity'],
'total' => (int) $row['total']
];
}
echo json_encode([
'ok' => true,
'summary' => $summary
]);
This endpoint returns processed information rather than every event record. It can support an accessible table or chart.
Choose endpoints from user needs
Useful endpoints might answer:
- Which fictional events require urgent review?
- How many events occurred in each category?
- Which events belong to a selected device?
- How has the event count changed over time?
Avoid creating many endpoints that expose the same unnecessary fields.
Check
- Invalid filters return HTTP 400 and a clear JSON error.
- User-supplied values are validated and passed through prepared statements.
- Totals are cast to numbers before encoding.
- Responses contain only the required fields.
- No personal information or credentials are exposed.
- All demonstration records are fictional.