MeowLoad Docs
On this page

PHP Code Examples

PHP can call the MeowLoad API with nothing installed: an HTTP stream context passed to file_get_contents is enough to see a first response, and the cURL extension bundled with practically every PHP build is what you want in production. Both examples below POST one URL to /openapi/v1/extract/post, read the JSON result and handle the error body, and both are plain PHP — no framework, no Composer package. Swap <your API key> for the key in your Developer Console first.

🚀 Using file_get_contents

Handy on a box where you cannot install anything: this needs only allow_url_fopen, which most hosts leave enabled. The catch is that PHP treats a 400 as a failure — without ignore_errors => true the call emits a warning, returns false, and discards the very body that tells you what went wrong. The status code is not returned either; it turns up only in the magic $http_response_header variable, which PHP populates in whatever scope made the request, so parse it immediately rather than passing the response somewhere else first. Your only timeout knob is default_socket_timeout (60 seconds unless your php.ini says otherwise), which is the main reason not to ship this version.

<?php

$apiUrl = "https://api.meowload.net/openapi/v1/extract/post";
$apiKey = "<your API key>";

$params = array("url" => "https://www.youtube.com/watch?v=dQw4w9WgXcQ");

$options = array(
    "http" => array(
        "header" =>
            "Content-Type: application/json\r\n" .
            "Authorization: Bearer " . $apiKey . "\r\n" .
            "Accept-Language: en",
        "method" => "POST",
        "content" => json_encode($params),
        "ignore_errors" => true, // Read the body on non-200 too, otherwise the error message is lost
    ),
);

$context = stream_context_create($options);
$response = file_get_contents($apiUrl, false, $context);

// Parse HTTP response status code
$statusLine = $http_response_header[0];
preg_match('{HTTP\/\S*\s(\d{3})}', $statusLine, $match);
$statusCode = $match[1];

$data = json_decode($response, true);

if ($statusCode == 200) {
    // Both title and text are optional: sites with titles return title, others return text
    $caption = $data['title'] ?? $data['text'] ?? '(none)';
    echo "✅ Extraction successful! Site: " . ($data['site'] ?? 'unknown') . ", caption: " . $caption . "\n";

    foreach ($data['medias'] as $media) {
        echo "- " . $media['media_type'] . ": " . $media['resource_url'] . "\n";
        // Resolutions / audio tracks live in variants; both video_url and audio_url present means a split stream
        foreach ($media['variants'] ?? array() as $variant) {
            $split = (!empty($variant['video_url']) && !empty($variant['audio_url'])) ? " (needs merging)" : "";
            echo "    " . ($variant['quality_label'] ?? 'default') . $split . "\n";
        }
    }
} else {
    echo "❌ Request failed (" . $statusCode . "): " . $data['message'] . "\n";
    if (!empty($data['retryable'])) {
        echo "   Code " . $data['code'] . " is transient; retry later\n";
    }
}
?>

📦 Using cURL

This is the one to ship. cURL hands you the status code through curl_getinfo, keeps the body out of the output buffer as long as CURLOPT_RETURNTRANSFER is set, and lets you bound the call with CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT. Check curl_errno($ch) before decoding: on a transport failure curl_exec returns false, json_decode turns that into null, and the exception you raise then blames the API for what was really a DNS or TLS problem. Wrapping the failure in an exception that carries code and retryable, as below, keeps the retry decision at the call site — retry only when retryable is true, and back off on 429 instead of hammering, since one key is capped at 1,200 requests per minute. Extracting many links in a loop? Reuse a single $ch handle rather than calling curl_init each time, so the TLS handshake is paid once.

<?php

class ExtractException extends Exception {
    public $code;
    public $retryable;

    public function __construct($message, $statusCode, $code = null, $retryable = false) {
        parent::__construct($message, $statusCode);
        $this->code = $code;
        $this->retryable = $retryable;
    }
}

function extractPost($url, $apiKey) {
    $apiUrl = "https://api.meowload.net/openapi/v1/extract/post";

    $ch = curl_init($apiUrl);

    $payload = json_encode(array("url" => $url));

    curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Authorization: Bearer ' . $apiKey,
        'Accept-Language: en'
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $response = curl_exec($ch);
    $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $data = json_decode($response, true);

    if ($statusCode == 200) {
        return $data;
    }
    // 400 carries code and retryable; 401/402/422/429 are identified by the status alone
    throw new ExtractException(
        $data['message'],
        $statusCode,
        $data['code'] ?? null,
        !empty($data['retryable'])
    );
}

// Usage
try {
    $result = extractPost("https://www.youtube.com/watch?v=dQw4w9WgXcQ", "<your API key>");
    $caption = $result['title'] ?? $result['text'] ?? '(none)';
    echo "✅ Extraction successful! Caption: " . $caption . ", media count: " . count($result['medias']) . "\n";
} catch (ExtractException $e) {
    echo "❌ Error (" . $e->getCode() . "): " . $e->getMessage() . "\n";
    if ($e->retryable) {
        echo "   Code " . $e->code . " is transient; retry later\n";
    }
}
?>

The quickstart is the shortest path from a fresh key to a working call, and error codes maps every failure to a cause and tells you whether retrying it is pointless. Field-by-field documentation for the endpoint used here lives on the single post API page and in the API reference. Before a batch job, have it read your remaining balance through the credits API — only successful extractions are charged, but a run that hits zero mid-way starts collecting HTTP 402 responses instead of results.