Talk to any API without ever configuring an HTTP client.

Fetch brings the fetch() you already know from JavaScript to PHP — with axios-style responses, JSON handled for you, and nothing to set up. In Leaf, Laravel, Slim, WordPress, or a single .php file.

app.php

$res = fetch([
    'method' => 'GET',
    'url'    => 'https://api.todos.dev/users/1',
    'data'   => [],
]);

// $res->data powers the app —>
todos.app
GET · 200
M

Mika Field

Maker · Accra 🇬🇭

12 todos 4 done pro plan

"Shipping something small every day."

Mika's todos

+ 201 Created
Water the monstera
Refactor the router
Reply to Discord
Ship the fetch site 🚀

"I just want the data."

Every HTTP call in PHP starts the same way: pick a client, construct it, configure it, remember its option names, decode the JSON yourself. Fetch skips the ritual. One function, familiar from the browser, that returns your data already decoded — because calling an API should be the easiest line in your codebase, not a design decision.

No framework required

It runs in whatever you're building.

Fetch depends on exactly one thing — ext-curl — so it drops into any codebase without touching your dependency tree.

Leaf
Laravel
Symfony
Slim
WordPress
CodeIgniter
Plain PHP
+ your stack

Familiar by design

The API you've been using for years.

If you've written JavaScript, you already know Fetch. A URL gets you a GET. Shortcut methods cover the rest of HTTP. No client objects, no factories — a function you call.

Learn more →

shortcuts.php

// a url is a GET
$todos = fetch('https://api.todos.dev/todos');

// everything else reads exactly how you'd guess
fetch()->post('/todos', ['title' => 'Ship fetch site']);
fetch()->put('/todos/1', ['done' => true]);
fetch()->patch('/todos/1', ['title' => 'Shipped!']);
fetch()->delete('/todos/1');

When you need more

One config array, axios-style.

The moment a request outgrows a one-liner, pass an array instead. Method, URL, body, headers — the same shape axios taught a generation of developers, so nothing needs re-learning.

Learn more →

request.php

$response = fetch([
    'method'  => 'PUT',
    'url'     => 'https://api.todos.dev/todos/1',
    'data'    => [
        'title' => 'Take a break',
        'done'  => true,
    ],
    'headers' => ['X-Request-Id' => 'abc-123'],
]);

$response->status;  // 200
$response->data;    // decoded body

Bodies, forms & queries

Send arrays. Fetch does the encoding.

Request bodies go out as JSON automatically. Need a classic form post? Set the content type and the same array ships urlencoded. On GET requests, your data becomes the query string — nested arrays included.

Learn more →

encoding.php

// arrays post as JSON — no json_encode in sight
fetch()->post('/orders', ['sku' => 'leaf-tee', 'qty' => 2]);

// same array, classic form encoding
fetch([
    'method'  => 'POST',
    'url'     => '/legacy/checkout',
    'data'    => ['sku' => 'leaf-tee'],
    'headers' => ['Content-Type' => 'application/x-www-form-urlencoded'],
]);

// on GET, data becomes ?page=2&tags[0]=php
fetch(['url' => '/posts', 'data' => ['page' => 2, 'tags' => ['php']]]);

Batteries where it counts

Everything around the request, handled.

The details that usually push you back to a heavyweight client — already covered, still one function.

Auth in one key

Basic auth is config, bearer is a header. No plugins.

'auth' => ['username' => ...,

         'password' => ...]

Base URLs

Point Fetch at your API once — every call after is a path.

Fetch::baseUrl('https://api.todos.dev');

fetch()->get('/todos');

One response shape

Data, status, headers and the original request — always there.

$res->data   $res->status

$res->headers $res->request

Raw when you want it

JSON is decoded by default — flip one flag for the raw body.

'rawResponse' => true

Full curl access

An escape hatch to every curl option, when you need the metal.

'curl' => [CURLOPT_REFERER => ...]

Your AI already knows it

Assistants have seen millions of fetch() calls — they write correct requests on the first try. One dependency (ext-curl), zero surprises.

Your next API call is one line away.

install

composer require leafs/fetch

# or: leaf install fetch