phpZenfolio is a PHP wrapper class for the Zenfolio API. The intention of this class is to allow PHP application developers to quickly and easily interact with the Zenfolio API in their applications, without having to worry about the finer details of the API.

Not already a Zenfolio user? Here, have a $5 discount off your first year on me by registering using this code:


The development of phpZenfolio takes place in my free time. If you find phpZenfolio useful and found it has saved you a lot of time, I’d really appreciate it if you bought me a coffee or two.

Buy me a Coffee



The recommended method of installing phpZenfolio is using Composer. If you have Composer installed, you can install phpZenfolio and all its dependencies from within your project directory:

$ composer require lildude/phpzenfolio

Alternatively, you can add the following to your project’s composer.json:

    "require": {
        "lildude/phpzenfolio": "^2.0"

.. and then run composer update from within your project directory.

If you don’t have Composer installed, you can download it using:

$ curl -s http://getcomposer.org/installer | php

Basic Usage

phpZenfolio follows the PSR-1, PSR-2 and PSR-4 conventions, which means you can easily use Composer’s autoloading to integrate phpZenfolio into your projects.

// This file is generated by Composer
require_once 'vendor/autoload.php';

$client = new phpZenfolio\Client('My Cool App/1.0 (http://app.com)'));
$photoset = $client->LoadPhotoSet(12345, 'Level1');

From the $client object, you can access all the Zenfolio API methods.

More In-depth Usage Details

Instantiating the Client

The phpZenfolio\Client() constructor takes two arguments:

  • AppName - Required.

    The name, version and URL of the application you have built using the phpZenfolio. There is no required format, but something like My Cool App/1.0 (http://my.url.com) would be very useful.

  • An array of options - Optional.

    The options you pass here become the default options applied to all requests by default, unless explicitly overwritten elsewhere, and can be made up of any combination of the following options:

    • api_version - (string) The API version you wish to use. This defaults to 1.8 as this is the only version of the API this version of phpZenfolio is compatible with. This is really only for “future proofing”.

    • proxy - (string) Configure all phpZenfolio requests to pass through a proxy. See the “Access Zenfolio via a Proxy” section for more details.

    • debug - (boolean) Enables Guzzle’s debug output. This is only really useful during development.

Additionally, you can pass any Guzzle request option though debug and proxy are probably the only options you may want to set.

Interacting with the Zenfolio API

Once you’ve instantiated an instance of the phpZenfolio\Client, you can use any of the Zenfolio methods, exactly as they’re documented, to interact with the API. This means you will need to pass the arguments in the order, case and form that Zenfolio expects.

Remember: ALL function names and arguments ARE case sensitive and the order of arguments is important.

Note: phpZenfolio does not currently support asynchronous requests, though as we now rely on Guzzle, this shouldn’t be too hard to implement in future (PRs welcome :wink:).

Creating Objects and Making Changes

Some of the Zenfolio API methods, like CreatePhotoSet(), or UpdatePhoto() require an Updater object to be passed as one of the arguments. phpZenfolio allows you to pass the object either as an associative array:

$photoSetUpdater = array(
    'Title' => 'PhotoSet Title',
    'Caption' => 'PhotoSet Caption via API',
    'Keywords' => array('Keyword1', 'keyword2'),
    'Categories' => array(),
    'CustomReference' => 'testing/test-photoset'
$client->CreatePhotoSet(12345, 'Gallery', $photoSetUpdater);

… or as a standard class object:

$photoSetUpdater = new stdClass();
$photoSetUpdater->Title = 'PhotoSet Title';
$photoSetUpdater->Caption = 'PhotoSet Caption via Object';
$photoSetUpdater->Keywords = array('Keyword1', 'keyword2');
$photoSetUpdater->Categories = array();
$photoSetUpdater->CustomReference = 'testing/test-photoset';
$client->CreatePhotoSet(12345, 'Gallery', $photoSetUpdater);

All data returned by the method call is returned as the API documents it.


Many of the Zenfolio API methods are open to all users, whether they have Zenfolio accounts or not, however anything private or related to modification requires authentication.

The Zenfolio API provides two methods of authentication: Plain-Text and Challenge-Response. Both are equally good with the slightly more secure method being the Challenge-Response method as your password never travels across the wire.

phpZenfolio allows you to use the API methods as documented, however to make things easy, a single login() method exists to allow you to authenticate using either of these authentication methods:

  • Challenge-Response (default):

    $client->login('[USERNAME]', '[PASSWORD]');
  • Plain-Text:

    // Setting the third argument to `true` confirms you want to use plain-text
    $client->login('[USERNAME]', '[PASSWORD]', true);

Both methods use HTTPS/SSL for the authentication step to ensure your username and password are encrypted when transmitted to Zenfolio.

The login() method returns the authentication token. You can store this and re-use it in future requests using phpZenfolio’s setAuthToken() method:

$client = new phpZenfolio\Client('My Cool App/1.0 (http://app.com)'));

Keep in mind that the authentication token is only valid for slightly more than 24 hours. If you expect your application to run for longer than 24 hours, it needs to periodically reauthenticate to obtain a fresh authentication token.

Additionally, keep in mind that when authenticating, you will gain full access to your content and only gain access to the albums, collections and photos of other users granted to you by that user. This means methods like [LoadGroupHierarchy()](http://www.zenfolio.com/zf/help/api/ref/methods/loadgrouphierarchy) and [LoadPublicProfile()](http://www.zenfolio.com/zf/help/api/ref/methods/loadpublicprofile) will only return information if the user in question has explicitly granted you access to their root photoset group or if they have made their root photoset group public.

Uploading & Replacing Images

Uploading is very easy. You can either upload an image from your local system using the phpZenfolio supplied upload() method, or from a location on the web using the API’s CreatePhotoFromUrl() method.

Upload a Local File:

To upload from your local filesystem using the phpZenfolio upload() method, you will need to have logged into Zenfolio via the API using the login() method and have the photoset object, the PhotoSetId, or it’s UploadUrl as returned by the API.

Then it’s a matter of calling the method with the various optional parameters.

  • Upload using the photoset object:

    $client->upload($photoset, "/path/to/image.jpg");
  • Upload using the PhotoSetId:

    $client->upload(123456, "/path/to/image.jpg");
  • Upload using the UploadUrl:

    $client->upload('http://up.zenfolio.com/....', '/path/to/image.jpg');

At this time, the only supported options you can pass at the time of uploading are a filename, the type and the modified parameter which takes a RFC2822 formatted date string…

$client->upload(123456, '/path/to/image.mpg',
    ['filename' => 'newfilename.mpg',
     'modified' => 'Thu, 14 Jan 2010 13:08:07 +0000',
     'type' => 'video']);

If you don’t specify a filename, the original filename is used.

Upload an Image from a URL:

Uploading to Zenfolio using a URL is done purely by the Zenfolio CreatePhotoFromUrl() API method:

$client->CreatePhotoFromUrl(12344, 'http://www.example.com/images/image.jpg');

You can find full details on the options this method accepts in the CreatePhotoFromUrl documentation.

Unfortunately, there is no way to pass things like the photo title etc at the time of upload. You will need to set these later using the UpdatePhoto() method.

Replacing Images

In order to replace a photo, you will need to upload a new photo and then replace the old photo with the new using the Zenfolio ReplacePhoto() API method.

Other Notes

Caching API Responses

Caching has been removed from phpZenfolio as the headers in the Zenfolio API responses discourage caching and now phpZenfolio is using Guzzle, you can take advantage of much better Guzzle-friendly middleware implementations, like guzzle-cache-middleware, that better tie-in with the various frameworks you may already be using.

In order to use one of these middleware caching mechanisms, you’ll need to create and pass a handler stack with the cache middleware you plan to use when instantiating the phpZenfolio client. For example:

$handler_stack = HandlerStack::create();
$handler_stack->push(new YourChosenCachingMiddleware(), 'cache');
$client = new phpZenfolio\Client('My Cool App/1.0 (http://app.com)', ['handler' => $handler_stack]);

Keeps in mind that phpZenfolio uses POST to the same URL for all requests. You may need to take this into account when configuring your caching implementation.

Please refer to your chosen caching implementation documentation for further details on how to use and implement that side of things with Guzzle.

Access Zenfolio via a Proxy

Accessing Zenfolio with phpZenfolio through a proxy is possible by passing the proxy option when instantiating the client:

$client = new phpZenfolio\Client('My Cool App/1.0 (http://app.com)', ['proxy' => 'http://[PROXY_ADDRESS]:[PORT]']));

All your requests will pass through the specified proxy on the specified port.

If you need a username and password to access your proxy, you can include them in the URL in the form: http://[USERNAME]:[PASSWORD]@[PROXY_ADDRESS]:[PORT].

Image URLs Helper

To make it easy to obtain the direct URL to an image, phpZenfolio supplies an imageURL() method that takes the Photo object as returned by methods like LoadPhoto() and LoadPhotoSetPhotos() and an integer for the desired photo size where the integer is one of those documented at http://www.zenfolio.com/zf/help/api/guide/download.

For example:

$client = new phpZenfolio\Client('My Cool App/1.0 (http://app.com)');
$photos = $client->LoadPhotoSetPhotos([PHOTOSETID], [STARTINGINDEX], [NUMBEROFPHOTOS]);
foreach ($photos as $photo) {
    echo '<img src="'.phpZenfolio\Client::imageUrl($photo, 1).'" />';


phpZenfolio comes with four examples to help get you on your way.

  • example-popular.php illustrates how to obtain the 96 most popular galleries and display their title image linking to each individual gallery.
  • example-login.php illustrates how to login and display the images in your first photoset or collection.
  • example-user.php illustrates how to display the first 96 public photos of the specified user’s first public photoset found.
  • example-create-photoset.php illustrates how to create a new gallery photoset in the authenticated user’s root photoset group, and upload an image to this gallery.

Reporting Issues

If you encounter a problem with phpZenfolio, please feel free to raise an issue. Please state which version of phpZenfolio you are using and ideally provide a small code snippet or link to a gist that can be used to reproduce the issue.

Getting Help

The best way to get help with implementing phpZenfolio into your projects is to open an issue. This allows you to easily search for other issues where others may have asked to the same questions or hit the same problems and if they haven’t, your issue will add to the resources available to others at a later date.

Please don’t be shy. If you’ve got a question, problem or are just curious about something, there’s a very good chance someone else is too, so go ahead and open an issue and ask.

If you need a quick bit of help or just want to say “Hi and thanks”, please use Twitter with the #phpZenfolio hashtag or tweet me directly @lildude.


Found a bug or want to make phpZenfolio even better? Please feel free to open a pull request with your changes, but be sure to check out the CONTRIBUTING.md first for some tips and guidelines. No pull request is too small.


All notable changes to this project are documented in CHANGELOG.md.


All code is licensed under the MIT License and all documentation is licensed under the CC BY 4.0 license.