Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use json_encode() to turn a PHP value into JSON, send that string as the POST body, and set Content-Type: application/json. In PHP, you can do this with cURL or the HTTP stream wrapper. If you are receiving JSON in PHP, read the raw body from php://input—not $_POST.

Send JSON with PHP cURL

cURL is a good fit when your PHP application needs to inspect transport errors and the HTTP status separately. This complete example encodes an associative array, posts its JSON representation, and captures the response body:

<?php
$data = [
    'name' => 'Ada',
    'active' => true,
];

$json = json_encode($data, JSON_THROW_ON_ERROR);

$ch = curl_init('https://api.example.test/endpoint');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Accept: application/json',
]);

$response = curl_exec($ch);
if ($response === false) {
    $error = curl_error($ch);
    curl_close($ch);
    throw new RuntimeException('Request failed: ' . $error);
}

$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status < 200 || $status >= 300) {
    throw new RuntimeException(
        'API returned HTTP ' . $status . ': ' . $response
    );
}

// Decode only if this endpoint returns JSON.
$result = json_decode($response, true, 512, JSON_THROW_ON_ERROR);

Replace https://api.example.test/endpoint and the payload with the endpoint and schema documented by the API you are calling. CURLOPT_POSTFIELDS receives the JSON string itself; do not pass the PHP array there if you intend to send JSON. The explicit content type tells the server how to interpret the body. Accept expresses that the client would like a JSON response, but the API may define a different response format.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

CURLOPT_RETURNTRANSFER makes curl_exec() return the response body instead of writing it directly to output. A transport-level failure returns false, so the example checks curl_error(). An HTTP error status is different: the request may have reached the server and received a response, so the example checks the status code separately and includes the response body in the exception for diagnosis. Adapt exception handling to your application rather than exposing raw API responses to end users.

The example decodes the response as JSON because many APIs return JSON, but that is an endpoint contract, not a guarantee of every POST request. If the endpoint returns an empty body or another format, remove or change the decoding step. Keep the status and body available long enough to handle the response the way that API specifies.

Send JSON with the HTTP stream wrapper

PHP’s HTTP stream wrapper can make the same request without building it through cURL options. Configure the method, headers, and body in a stream context:

<?php
$data = [
    'name' => 'Ada',
    'active' => true,
];

$json = json_encode($data, JSON_THROW_ON_ERROR);

$options = [
    'http' => [
        'method' => 'POST',
        'header' => [
            'Content-Type: application/json',
            'Accept: application/json',
        ],
        'content' => $json,
        'ignore_errors' => true,
    ],
];

$context = stream_context_create($options);
$response = file_get_contents(
    'https://api.example.test/endpoint',
    false,
    $context
);

if ($response === false) {
    throw new RuntimeException('Could not read a response from the endpoint.');
}

// HTTP response headers are available in $http_response_header
// in the scope where file_get_contents() was called.
$statusLine = $http_response_header[0] ?? '';
if (!preg_match('/s([0-9]{3})s/', $statusLine, $matches)) {
    throw new RuntimeException('Could not determine the HTTP status.');
}
$status = (int) $matches[1];

if ($status < 200 || $status >= 300) {
    throw new RuntimeException(
        'API returned HTTP ' . $status . ': ' . $response
    );
}

The context’s method, header, and content define the POST request. The ignore_errors setting allows the call to return the response body for HTTP error statuses, which the code then evaluates explicitly. If the call itself fails, file_get_contents() can return false; distinguish that from an HTTP error response that contains a body.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Stream-context options and available behavior can vary with the PHP runtime. Check the target environment’s PHP documentation when relying on version-specific details, and verify that the HTTP wrapper is suitable for the deployment. Neither stream functions nor cURL can determine an API’s required authentication, request fields, or response schema on their own.

Choose cURL or a stream context

Consideration cURL HTTP stream context
Construct the request Set cURL options for the POST body and headers. Set the HTTP context method, headers, and content.
Handle the response Use CURLOPT_RETURNTRANSFER; check transport errors and inspect the HTTP status. Check the stream function’s return value and inspect response metadata such as the status headers.
Deployment fit Confirm the cURL extension is available in the PHP runtime. Confirm the HTTP wrapper and its options are suitable for the environment.
What neither option decides The endpoint URL, authentication method, accepted payload, and response rules are API-specific.

The PHP documentation describes how to construct requests with both approaches; it does not establish a universal performance winner. Pick based on the extensions and controls available in your deployment and the way your application needs to handle transport failures and response metadata.

Encode the payload correctly

Build the request as a PHP array or object that matches the API’s expected JSON structure, then encode it once. For example, the array ['name' => 'Ada', 'active' => true] becomes a JSON object with a string and a boolean. An API expecting a list, nested object, or particular field names may reject a syntactically valid JSON body if its schema does not match.

PHP’s json_encode() requires string data to be UTF-8 encoded. By default, it returns a JSON string on success or false if encoding fails. The examples use JSON_THROW_ON_ERROR so encoding failure becomes an exception rather than allowing a false value to travel down the request path as though it were valid JSON. Confirm that your PHP runtime supports the syntax and constants used by your application.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Do not use http_build_query() to prepare a JSON body: that produces form-style key/value content, not JSON. Similarly, setting the JSON content type without actually sending JSON text creates a mismatch between the header and body.

Receive JSON in a PHP endpoint

If your PHP code is the server receiving the request, read the raw input stream and decode it. $_POST is documented for form-encoded and multipart form bodies; JSON request bodies are read from php://input.

<?php
$rawBody = file_get_contents('php://input');
$data = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);

if (!is_array($data)) {
    http_response_code(400);
    header('Content-Type: application/json');
    echo json_encode(['error' => 'Expected a JSON object or array']);
    exit;
}

// Validate required fields and types before using $data.

With the second argument to json_decode() set to true, JSON objects are returned as associative arrays. Decoding only parses the syntax; it does not validate your application’s required fields, types, authorization rules, or business logic. Validate those separately before acting on the data. If parsing can fail, catch the decoding exception and return an appropriate client error instead of allowing malformed input to become an unhandled server failure.

Common failures and how to fix them

  • The receiver sees an empty $_POST. This is expected for a JSON content type. Read php://input and decode the returned string.
  • The API says the body is malformed or missing. Check that json_encode() produced a string, that the encoded string is sent as the body, and that Content-Type: application/json is present. Do not substitute a URL-encoded form body.
  • json_encode() fails or returns false. Inspect the input values and their string encoding. PHP requires strings to be UTF-8; using JSON_THROW_ON_ERROR surfaces an encoding failure at the point it occurs.
  • curl_exec() returns false. Read curl_error() before closing the handle. This is a cURL/transport failure, not an HTTP response status.
  • The request runs, but the API rejects it. Inspect the HTTP status and response body, then verify the endpoint URL, authentication, payload schema, and API-specific status rules in that API’s documentation. A completed connection alone does not mean the API accepted the request.
  • The stream call returns false or hides useful error details. Distinguish failure to obtain a response from an HTTP error response. Configure error-body handling where appropriate and inspect the available response headers and body.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Practical reliability and cost considerations

Choose a timeout that fits the calling application and the endpoint’s expected response time; the correct value depends on your workload and is not established by the PHP request-construction examples. In production, log enough context to diagnose failures—such as the status, a request identifier if the API provides one, and a safely redacted error body—without logging secrets or sensitive payloads.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Retry behavior is also endpoint-specific. Before retrying a failed POST, determine whether the operation can be repeated safely or whether the API supports an idempotency mechanism. A timeout does not necessarily prove that the server did not process the request. PHP’s general request mechanics do not establish a universal retry policy, performance comparison, or cost: check the target API’s contract and billing terms.

Or skip the browser setup

If the task behind your request is capturing a webpage rather than posting JSON to your own API, ScreenshotNeo is a website screenshot API and MCP server. Its request is a GET to the screenshot endpoint, not a JSON POST; use this cURL example to save a WebP capture:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo documentation for request options. Before capture, it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; those steps can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses indicate the page verdict and billing status in headers. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots.

Sign up free for 1,000 screenshots a month—no card required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can PHP send JSON to an API that requires authentication?

Yes. Add the authentication header or other credentials in the way that API documents, and keep secrets out of source control and logs.

Does setting Accept: application/json make the request body JSON?

No. The body format is indicated by Content-Type; Accept describes the response format the client can handle.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.