Cloudflare-API
view release on metacpan or search on metacpan
doc/cloudflare-api.xml view on Meta::CPAN
<?xml version="1.0" encoding="UTF-8"?>
<article version="5.0" xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Cloudflare::API: Using Cloudflare from Perl</title>
<author>
<personname><firstname>Andrew</firstname><surname>Speer</surname></personname>
</author>
<copyright><year>2026</year><holder>Andrew Speer</holder></copyright>
</info>
<section xml:id="introduction">
<title>Introduction</title>
<para>Want to list your R2 buckets, inspect a Worker, or create a D1 database from a Perl script? <classname>Cloudflare::API</classname> gives you a small client for those everyday Cloudflare management tasks. It uses <classname>HTTP::API::Core</...
<para>You can call the module from Perl or use the accompanying <command>cloudflare-api</command> command when a shell is more convenient. Both talk to Cloudflare's management API. Neither builds a Worker project or runs npm. The command can use ...
<para>If you only need a quick look, the next example shows the whole idea. The later sections explain credentials, resource methods, uploads, responses, and command-line options. A module-by-module reference is near the end.</para>
<programlisting language="perl">use strict;
use warnings;
use Cloudflare::API;
my $api_or=Cloudflare::API->new();
my $buckets_ar=$api_or->r2()->list_buckets();
my $scripts_ar=$api_or->workers()->list_scripts();</programlisting>
<para>With <envar>CLOUDFLARE_API_TOKEN</envar> and <envar>CLOUDFLARE_ACCOUNT_ID</envar> set, the client supplies the account path and bearer token. List methods follow Cloudflare's pages and return a flat array reference. Other JSON methods norma...
<note><para>The distribution requires Perl 5.10 or later, <classname>HTTP::API::Core</classname> 1.08 or later, and Perl HTTPS support through <classname>IO::Socket::SSL</classname>. Install it with a CPAN client in the usual way.</para></note>
</section>
<section xml:id="credentials">
<title>Credentials and account context</title>
<para>Create an API token in the Cloudflare dashboard with only the permissions and account or zone access your script needs. Cloudflare offers user tokens and account tokens; the endpoint and your account permissions determine which is appropria...
<programlisting language="sh"># Load CLOUDFLARE_API_TOKEN from your secret manager first.
export CLOUDFLARE_ACCOUNT_ID='your-account-id'
cloudflare-api --resource r2 --action list_buckets</programlisting>
<para>The account ID above is a placeholder. Load the token through your secret manager so it does not enter a repository or a pasted command. The module also accepts explicit <literal>token</literal> and <literal>account_id</literal> constructor...
<programlisting language="perl">my $api_or=Cloudflare::API->new(
token => $ENV{'CLOUDFLARE_API_TOKEN'},
account_id => $ENV{'CLOUDFLARE_ACCOUNT_ID'},
timeout => 30
);</programlisting>
<para>You can omit <literal>account_id</literal> for account and zone lookup, but account-scoped methods need it. The command has <option>--account-id</option> to choose another account while continuing to read the token from the environment.</pa...
<warning><para>An API token is a credential, even when it is short lived. Do not commit it, paste it into command-line arguments, print it in logs, or include it in error reports. Use a narrowly scoped token; a token that can edit a resource can ...
<para>If you have logged into Wrangler locally, the command can ask Wrangler for a token for each invocation. Wrangler refreshes an expired OAuth token before returning it:</para>
<programlisting language="sh">cloudflare-api --auth=wrangler --resource workers --action list_scripts</programlisting>
<para>For an account-scoped named method, <option>--auth=wrangler</option> also uses <command>wrangler whoami --json</command> to obtain the account ID when the login has exactly one available account. If it has several, select one with <option>-...
<tip><para>For unattended scripts, use a dedicated, limited API token supplied by a secret manager. The Wrangler shortcut is best suited to an interactive session you control.</para></tip>
</section>
<section xml:id="perl-synopsis">
<title>A first Perl script</title>
<para>Let's list two resources without changing anything. Resource accessors are named for the Cloudflare service, and their methods follow the action you want to perform:</para>
<programlisting language="perl">use strict;
use warnings;
use Cloudflare::API;
my $api_or=Cloudflare::API->new();
my $zones_ar=$api_or->zones()->list(status => 'active');
my $namespaces_ar=$api_or->kv()->list_namespaces(per_page => 20);
foreach my $zone_hr (@$zones_ar) {
print $zone_hr->{'name'}, "\n";
}</programlisting>
<para><methodname>zones()->list()</methodname> is not account-scoped; <methodname>kv()->list_namespaces()</methodname> is. List methods accept Cloudflare query fields as named arguments, follow every available page, and return a flat array ...
<para>To create something, pass Cloudflare's JSON request body as a hash reference. Here are two independent examples:</para>
<programlisting language="perl">my $bucket_hr=$api_or->r2()->create_bucket({
name => 'my-app-assets'
});
my $database_hr=$api_or->d1()->create_database({
name => 'my-app-data'
});</programlisting>
<caution><para>These calls create real Cloudflare resources. The module does not check whether a name already exists, make writes idempotent, or roll back later calls if a script fails.</para></caution>
</section>
<section xml:id="resource-api">
<title>Exploring the resource API</title>
<para>The client has accessors for <methodname>accounts()</methodname>, <methodname>zones()</methodname>, <methodname>workers()</methodname>, <methodname>r2()</methodname>, <methodname>kv()</methodname>, <methodname>d1()</methodname>, <methodname...
<section xml:id="read-and-write">
<title>Lists, detail, and changes</title>
<para>Most resources offer a familiar list, get, create, update, and delete pattern. For example, you can list R2 buckets, get one by name, then update or delete it using that name. Queues, Hyperdrive configurations, and Secrets Store resources...
<programlisting language="perl">my $bucket_hr=$api_or->r2()->get_bucket('my-app-assets');
my $queue_hr=$api_or->queues()->create_queue({ name => 'jobs' });
my $config_ar=$api_or->hyperdrive()->list_configs();</programlisting>
<para>Accounts and zones are useful starting points when you do not yet know an ID:</para>
<programlisting language="perl">my $accounts_ar=$api_or->accounts()->list();
my $zones_ar=$api_or->zones()->list(name => 'example.com');</programlisting>
<para>The methods that take a body pass the supplied hash reference to Cloudflare. Refer to the relevant Cloudflare API endpoint for its required fields and accepted values. The module deliberately leaves most endpoint-specific payload choices ...
</section>
<section xml:id="kv-and-d1">
<title>KV values and D1 queries</title>
<para>KV has a small convenience API for keys and raw values. The value methods are separate from the JSON management methods because the value body need not be JSON:</para>
<programlisting language="perl">my $kv_or=$api_or->kv();
$kv_or->put_value('namespace-id', 'greeting', 'Hello', expiration_ttl => 3600);
my $value=$kv_or->get_value('namespace-id', 'greeting');
my $keys_ar=$kv_or->list_keys('namespace-id', prefix => 'greet');</programlisting>
<para><methodname>get_value()</methodname> returns raw bytes. <methodname>put_value()</methodname> accepts either <literal>expiration</literal> or <literal>expiration_ttl</literal>, and writing a value replaces its previous expiration and metad...
<para>For D1, <methodname>query_sql()</methodname> keeps SQL parameters separate from the statement:</para>
<programlisting language="perl">my $rows_ar=$api_or->d1()->query_sql(
'database-id',
'SELECT id, name FROM people WHERE id = ?',
[42]
);</programlisting>
<para>The D1 REST result remains Cloudflare's array of query results. This is a management API call, not a DBI connection or a migration tool. Hyperdrive methods likewise manage connection configuration; SQL for a Hyperdrive-backed application ...
doc/cloudflare-api.xml view on Meta::CPAN
);</programlisting>
<warning><para><methodname>upload_script()</methodname> deploys immediately. If you want to inspect a version before activating it, use <methodname>upload_version()</methodname> with the same <literal>metadata</literal> and <literal>files</lite...
<para>Static assets can be uploaded from a directory, a list of files, or a URL-path map. The returned value includes a manifest and a short-lived completion JWT for the version metadata:</para>
<programlisting language="perl">my $assets_hr=$api_or->workers()->upload_assets(
'my-app', 'dist/site', prefix => '/docs'
);
my $version_hr=$api_or->workers()->upload_version('my-app',
metadata => {
main_module => 'worker.mjs',
compatibility_date => '2026-09-22',
assets => { jwt => $assets_hr->{'jwt'} },
bindings => [{ type => 'assets', name => 'ASSETS' }]
},
files => [{ name => 'worker.mjs', path => 'dist/worker.mjs' }]
);</programlisting>
<para>Neither <methodname>upload_assets()</methodname> nor <methodname>upload_version()</methodname> activates a new deployment. Asset uploads are assembled in memory, so consider the size of the files in a large upload. Treat the completion JW...
<para>Other Worker methods inspect scripts and versions, manage deployments and secret bindings, and control routes or the workers.dev subdomain. <methodname>download_script()</methodname> returns a raw response because its body is source conte...
</section>
</section>
<section xml:id="responses-and-errors">
<title>Responses, pagination, and errors</title>
<para>Cloudflare JSON responses commonly contain <literal>success</literal>, <literal>result</literal>, and sometimes <literal>result_info</literal>. A list method follows every available page and returns a flat array reference:</para>
<programlisting language="perl">my $buckets_ar=$api_or->r2()->list_buckets(per_page => 100);</programlisting>
<para>This is convenient for ordinary result sets, but it issues as many requests as necessary and keeps every item in memory. Use the corresponding <methodname>_page()</methodname> method when the result may be large. It returns a lazy <classnam...
<programlisting language="perl">my $page_or=$api_or->r2()->list_buckets_page(per_page => 100);
while (my $bucket_hr=$page_or->next()) {
print $bucket_hr->{'name'}, "\n";
}</programlisting>
<para>The corresponding <methodname>_page_response()</methodname> method makes exactly one request and returns the complete decoded Cloudflare response hash. Use it when you need <literal>result_info</literal>, page boundaries, or other response ...
<programlisting language="perl">my $page_hr=$api_or->r2()->list_buckets_page_response(per_page => 100);
my $buckets_ar=$page_hr->{'result'}{'buckets'};</programlisting>
<para>The exact response and pagination fields depend on the endpoint. A supplied <literal>page</literal> or <literal>cursor</literal> selects the starting point for a list method or paginator. <literal>full_response</literal> is unavailable for ...
<para>An HTTP or transport failure is thrown as an <classname>HTTP::API::Core::Error</classname>. A successful HTTP response whose decoded Cloudflare response says <literal>success: false</literal> is thrown as <classname>Cloudflare::API::Error</...
<programlisting language="perl">my $result_hr=eval { $api_or->r2()->get_bucket('my-app-assets') };
if (my $error=$@) {
if (ref($error) && $error->isa('Cloudflare::API::Error')) {
warn "Cloudflare rejected the request: $error\n";
}
else {
die $error;
}
}</programlisting>
<note><para>A missing account ID or invalid local argument may also raise an ordinary Perl exception before any request is made. Keep tokens and secret request bodies out of exception logs.</para></note>
</section>
<section xml:id="lower-level-api">
<title>When there is no named method</title>
<para>The named methods cover a useful part of Cloudflare's API, not every endpoint. Use <methodname>request()</methodname> for a JSON endpoint that does not yet have a wrapper. It accepts an HTTP method, a path relative to the Cloudflare API roo...
<programlisting language="perl">my $accounts_ar=$api_or->request('GET', '/accounts',
query => { per_page => 20 }
);
my $decoded_hr=$api_or->request_full('GET', '/accounts');
my $response_or=$api_or->response('GET', '/accounts');</programlisting>
<para><methodname>request()</methodname> unwraps <literal>result</literal>, <methodname>request_full()</methodname> keeps the complete decoded Cloudflare response, and <methodname>response()</methodname> returns the <classname>HTTP::API::Core::Re...
<important><para>Paths must start with one slash and cannot be absolute URLs, so a caller-supplied path cannot send the bearer token to another host. If you put a dynamic value in a low-level path, percent-encode that segment yourself. Named reso...
</section>
<section xml:id="command-line">
<title>Using the command-line script</title>
<para><command>cloudflare-api</command> is useful for a quick lookup or a shell script that already has credentials in its environment. Choose a resource and action for a named method. Output is pretty JSON unless you ask for <literal>dumper</lit...
<programlisting language="sh">cloudflare-api --resource zones --action list --param status=active
cloudflare-api --resource r2 --action list_buckets --full-response
cloudflare-api --resource zones --action list --output dumper</programlisting>
<para>A <option>--param</option> passes a named string argument, usually a list filter. <option>--arg</option> passes a positional string argument, in the order given. For a method expecting a JSON body, use a typed argument rather than a string:...
<programlisting language="sh">cloudflare-api --resource kv --action create_namespace \
--arg-json '{"title":"my-app-cache"}'
cloudflare-api --resource workers --action upload_assets \
--arg my-app --arg dist/site --param prefix=/docs</programlisting>
<para>For selected files, give the Worker name with <option>--arg</option>, then combine repeatable <option>--asset FILE</option>, <option>--asset-list-json FILE</option>, and <option>--asset-list-text FILE</option>. Use <option>--asset-list-stdi...
<programlisting language="sh">cloudflare-api --resource workers --action upload_assets \
--arg my-app --asset dist/index.html --asset-list-text images.txt \
--param prefix=/docs</programlisting>
<para>Typed positional forms include <option>--arg-bool</option>, <option>--arg-array</option>, <option>--arg-hash</option>, <option>--arg-json</option>, and <option>--arg-json-file</option>. Named forms include <option>--param-bool</option>, <op...
<para>The command deliberately makes one request for a list action unless <option>--paginate</option> is supplied. With <option>--paginate</option>, it returns an array of page results, preserving each page boundary. Without <option>--max-pages</...
<programlisting language="sh">cloudflare-api --resource kv --action list_namespaces \
--paginate --per-page 20 --max-pages 2 --full-response</programlisting>
<para>The lower-level form uses <option>--method</option> and <option>--path</option> instead of a resource and action:</para>
<programlisting language="sh">cloudflare-api --method GET --path /accounts --full-response</programlisting>
<para>Use <option>--help</option> for a short reminder, <option>--man</option> for the complete option reference, or <option>--version</option> for the installed version. The command also has <option>--account-id</option> and <option>--output jso...
<warning><para>The <option>--arg-dumper-file</option> and <option>--param-dumper-file</option> options evaluate the file as Perl code. Use them only with files you trust; prefer JSON for data from elsewhere. <option>--dump-opt</option> prints par...
</section>
<section xml:id="module-reference">
<title>Module reference</title>
<para>The examples above are a starting point. For the actual method names, arguments, and return conventions, follow the Markdown reference kept beside each module's Perl source. Those sidecars are the maintained method reference; this article s...
<variablelist>
<varlistentry>
<term><link xlink:href="lib/Cloudflare/API.pm.md"><classname>Cloudflare::API</classname></link></term>
<listitem><para>Constructor options, environment defaults, resource accessors, low-level requests, and response handling.</para></listitem>
</varlistentry>
<varlistentry>
<term><link xlink:href="lib/Cloudflare/API/Accounts.pm.md"><classname>Cloudflare::API::Accounts</classname></link></term>
<listitem><para>List accounts or retrieve one by ID.</para></listitem>
</varlistentry>
<varlistentry>
<term><link xlink:href="lib/Cloudflare/API/Zones.pm.md"><classname>Cloudflare::API::Zones</classname></link></term>
<listitem><para>List zones or retrieve one by ID.</para></listitem>
</varlistentry>
<varlistentry>
<term><link xlink:href="lib/Cloudflare/API/Workers.pm.md"><classname>Cloudflare::API::Workers</classname></link></term>
<listitem><para>Scripts, staged versions, deployments, assets, secrets, subdomains, and routes. Start here for upload arguments and return values.</para></listitem>
</varlistentry>
<varlistentry>
<term><link xlink:href="lib/Cloudflare/API/R2.pm.md"><classname>Cloudflare::API::R2</classname></link></term>
<listitem><para>Bucket management through the REST API.</para></listitem>
</varlistentry>
<varlistentry>
<term><link xlink:href="lib/Cloudflare/API/KV.pm.md"><classname>Cloudflare::API::KV</classname></link></term>
<listitem><para>Namespace management, key lists, and raw value reads and writes.</para></listitem>
</varlistentry>
<varlistentry>
<term><link xlink:href="lib/Cloudflare/API/D1.pm.md"><classname>Cloudflare::API::D1</classname></link></term>
<listitem><para>Database management and REST queries with separate SQL parameters.</para></listitem>
</varlistentry>
<varlistentry>
<term><link xlink:href="lib/Cloudflare/API/Queues.pm.md"><classname>Cloudflare::API::Queues</classname></link></term>
<listitem><para>Queue and consumer management.</para></listitem>
</varlistentry>
<varlistentry>
<term><link xlink:href="lib/Cloudflare/API/Hyperdrive.pm.md"><classname>Cloudflare::API::Hyperdrive</classname></link></term>
( run in 0.925 second using v1.01-cache-2.11-cpan-3f962061493 )