Structured Content 101: Find out how to make your content work harder (without your team working harder) →

PHP API client

Official(made by Sanity team)

By Espen Hovlandsdal

PHP library for the Sanity API


PackagistBuild Status

PHP library for the Sanity API


sanity-php requires PHP >= 5.6, with the json module installed.


You can install the library via Composer. Run the following command:

composer require sanity/sanity-php

To use the library, use Composer's autoload:

require_once 'vendor/autoload.php';


Instantiating a new client

use Sanity\Client as SanityClient;

$client = new SanityClient([
  'projectId' => 'your-project-id',
  'dataset' => 'your-dataset-name',
  // Whether or not to use the API CDN for queries. Default is false.
  'useCdn' => true,
  // If you are starting a new project, using the current UTC date is usually
  // a good idea. See "Specifying API version" section for more details
  'apiVersion' => '2019-01-29',

Using an authorization token

$client = new SanityClient([
  'projectId' => 'your-project-id',
  'dataset' => 'your-dataset-name',
  'useCdn' => false,
  'apiVersion' => '2019-01-29',
  // Note that you cannot combine a token with the `useCdn` option set to true,
  // as authenticated requests cannot be cached
  'token' => 'sanity-auth-token',

Specifying API version

Sanity uses ISO dates (YYYY-MM-DD) in UTC timezone for versioning. The explanation for this can be found in the documentation

In general, unless you know what API version you want to use, you'll want to set it to todays UTC date. By doing this, you'll get all the latest bugfixes and features, while preventing any timezone confusion and locking the API to prevent breaking changes.

Note: Do not be tempted to use a dynamic value for the apiVersion. The whole reason for setting a static value is to prevent unexpected, breaking changes.

In future versions, specifying an API version will be required. For now, to maintain backwards compatiblity, not specifying a version will trigger a deprecation warning and fall back to using v1.

Fetch a single document by ID

$document = $client->getDocument('someDocumentId');

Performing queries

$results = $client->fetch(
  '*[_type == $type][0...3]', // Query
  ['type' => 'product'] // Params (optional)

foreach ($product in $results) {
  echo $product['title'] . '\n';

See the query documentation for more information on how to write queries.

Creating documents

$doc = [
  '_type' => 'bike',
  'name'  => 'Bengler Tandem Extraordinaire',
  'seats' => 2,

$newDocument = $client->create($doc);
echo 'Bike was created, document ID is ' . $newDocument['_id'];

This creates a new document with the given properties. It must contain a _type attribute, and may contain a _id attribute. If an ID is specified and a document with that ID already exist, the mutation will fail. If an ID is not specified, it will be auto-generated and is included in the returned document.

Creating a document (if it does not exist)

As noted above, if you include an _id property when calling create() and a document with this ID already exists, it will fail. If you instead want to ignore the create operation if it exists, you can use createIfNotExists(). It takes the same arguments as create(), the only difference being that it requires an _id attribute.

$doc = [
  '_id'   => 'my-document-id',
  '_type' => 'bike',
  'name'  => 'Amazing bike',
  'seats' => 3,

$newDocument = $client->createIfNotExists($doc);

Replacing a document

If you don't care whether or not a document exists already and just want to replace it, you can use the createOrReplace() method.

$doc = [
  '_id'   => 'my-document-id',
  '_type' => 'bike',
  'name'  => 'Amazing bike',
  'seats' => 3,

$newDocument = $client->createOrReplace($doc);

Patch/update a document

use Sanity\Exception\BaseException;

try {
  $updatedBike = $client
    ->patch('bike-123') // Document ID to patch
    ->set(['inStock' => false]) // Shallow merge
    ->inc(['numSold' => 1]) // Increment field by count
    ->commit(); // Perform the patch and return the modified document
} catch (BaseException $error) {
  echo 'Oh no, the update failed: ';

Todo: Document all patch operations

Delete a document

use Sanity\Exception\BaseException;

try {
} catch (BaseException $error) {
  echo 'Delete failed: ';

Multiple mutations in a transaction

$namePatch = $client->patch('bike-310')->set(['name' => 'A Bike To Go']);

try {
    ->create(['name' => 'Bengler Tandem Extraordinaire', 'seats' => 2])

  echo 'A whole lot of stuff just happened!';
} catch (BaseException $error) {
  echo 'Transaction failed:';

Clientless patches & transactions

use Sanity\Patch;
use Sanity\Transaction;

// Patches:
$patch = new Patch('<documentId>');
$patch->inc(['count' => 1])->unset(['visits']);

// Transactions:
$transaction = new Transaction();
  ->create(['_id' => '123', 'name' => 'FooBike'])


An important note on this approach is that you cannot call commit() on transactions or patches instantiated this way, instead you have to pass them to client.mutate().

Get client configuration

$config = $client->config();
echo $config['dataset'];

Set client configuration

$client->config(['dataset' => 'newDataset']);

The new configuration will be merged with the existing, so you only need to pass the options you want to modify.

Rendering block content

When you use the block editor in Sanity, it produces a structured array structure that you can use to render the content on any platform you might want. In PHP, a common output format is HTML. To make the transformation from the array structure to HTML simpler, we include a helper class for this within the library.

If your content only contains the basic, built-in block types, you can get rendered HTML like this:

use Sanity\BlockContent;

$document = $client->getDocument('some-doc');
$article = $document['article']; // The field that contains your block content

$html = BlockContent::toHtml($article, [
    'projectId'    => 'abc123',
    'dataset'      => 'bikeshop',
    'imageOptions' => ['w' => 320, 'h' => 240]

If you have some custom types, or would like to customize the rendering, you may pass an associative array of serializers:

$html = BlockContent::toHtml($article, [
  'serializers' => [
    'listItem' => function ($item, $parent, $htmlBuilder) {
      return '<li class="my-list-item">' . implode('\n', $item['children']) . '</li>';
    'geopoint' => function ($item) {
      $attrs = $item['attributes']
      $url = ''
      $url .= $attrs['lat'] . ',' . $attrs['lng'];
      return '<iframe class="geomap" src="' . $url . '" allowfullscreen></iframe>'
    'pet' => function ($item, $parent, $htmlBuilder) {
      return '<p class="pet">' . $htmlBuilder->escape($item['attributes']['name']) . '</p>';


sanity-php follows the PSR-2 Coding Style Guide. Contributions are welcome, but must conform to this standard.


MIT-licensed. See LICENSE


Categorized in

Other plugins by author

Mux input
- has 6 likes

v3 Ready

Upload videos to Mux directly from the studio.

Go to Mux input

Google Maps input

Official(made by Sanity team)
v3 Ready

Use geo-related input types using Google Maps.

Espen Hovlandsdal
Go to Google Maps input

PortableText to React
- has 10 likes

Official(made by Sanity team)

Render PortableText content in React

Espen Hovlandsdal
Go to PortableText to React


An opinionated, enhanced plugin development experience.

Espen Hovlandsdal
Go to Sanipack