Download the PHP package picqer/exact-php-client without Composer

On this page you can find all versions of the php package picqer/exact-php-client. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package exact-php-client

Exact Online PHP client

Tests Latest version Downloads

A PHP client for the Exact Online REST API. Use it to create invoices, book journal entries, sync items, stock and relations, and anything else the API offers.

The client follows the naming of the Exact Online API. Every endpoint is a class with the same name, and every field has the same name as in the API. The Exact API reference is therefore also the reference for this library.

Created and maintained by Picqer.

Contents

Installation

Requires PHP 7.4 or higher and Guzzle 7.15.2 or higher.

Quick start

  1. Create an app in the Exact Online App Center to get a client ID and client secret, and set the redirect URL to a page of your application.
  2. Send the user to Exact to authorize your app. Exact redirects back to your redirect URL with an authorization code.
  3. Exchange the code for tokens, store them, and start calling the API.

Once you have tokens, using the API looks like this:

A complete, runnable version of the authorization flow is in example/example.php.

Authentication

Exact Online uses OAuth 2.0. A user only needs to authorize your app once. After that, the library keeps the connection alive with the refresh token.

1. Redirect the user to Exact

Use $connection->getAuthUrl() instead if you want to do the redirect yourself. Use setForceLogin(true) to always show Exact's login screen.

2. Handle the callback

Exact redirects back to your redirect URL with a code query parameter. The code can only be used once, so exchange it for tokens right away:

3. Connect with stored tokens

For every later request, create the connection from your stored tokens:

The access token is valid for about 10 minutes. The library refreshes it automatically just before it expires, using the refresh token.

[!IMPORTANT] Always set the token update callback. Every refresh gives you a new refresh token and invalidates the old one. If you don't store the new refresh token, your next connection uses an invalid token, and the user has to authorize your app again.

Refreshing tokens from multiple processes

If several processes (queue workers, cron jobs, web requests) share the same tokens, two of them can refresh at the same time. One of them then ends up with an invalidated refresh token. Prevent this with a lock around the refresh:

Divisions (administrations)

Almost every endpoint works within one division (administration). Set it explicitly:

If you don't, the library asks Exact for the user's current division on every new connection. That costs an extra API call, and the result changes when the user switches administrations in Exact Online.

List the divisions the user has access to with the Division entity (system/Divisions), or all divisions of the license with AllDivision (system/AllDivisions).

Other countries

The library connects to the Dutch Exact Online site by default. For other countries, set the base URL before authorizing:

Country Base URL
Netherlands https://start.exactonline.nl
Belgium https://start.exactonline.be
Germany https://start.exactonline.de
United Kingdom https://start.exactonline.co.uk
United States https://start.exactonline.com
Spain https://start.exactonline.es
France https://start.exactonline.fr

Reading data

Every endpoint is a class in Picqer\Financials\Exact. See src/Picqer/Financials/Exact for the full list.

find(), first(), get() and filter() return new objects. They don't fill the object you call them on.

Filter options

Filters use the OData syntax that Exact Online supports. Write GUIDs as guid'…' and strings as '…'. The Filter column in the API reference shows which fields you can filter on.

Large data sets

A regular endpoint returns 60 records per page. get() and filter() fetch all pages, which can take a lot of memory. Use the generator variants to handle records one at a time:

getAsGenerator() works the same way. To control the paging yourself, for example to spread requests over time:

Sync and bulk endpoints

For large data sets, Exact recommends the sync and bulk endpoints. They return 1000 records per page.

Sync endpoints (classes named Sync…) return records that are new or changed since a given Timestamp. Store the highest Timestamp you've seen and continue from there next time. The timestamp is a row version, not a date.

Deleted records are available through SyncDeleted.

Bulk endpoints (classes named Bulk…) require you to select the fields you need:

Creating, updating and deleting

Set fields as properties and call save(). A record without a primary key is created (POST), a record with one is updated (PUT). After saving, the object contains the record as Exact returned it, including the new ID.

Only fields that exist in the API are sent. Unknown properties are ignored.

Records with lines

Some endpoints, like sales invoices and sales orders, need their lines in the same request when you create them:

Lines are only sent when creating a record. To change the lines of an existing record, use the line endpoint, such as SalesInvoiceLine.

Related data and files

Related collections

Related collections such as SalesInvoiceLines are loaded the first time you use them:

That is an extra API call per record. When you need the lines of many records, include them in the same request with $expand:

If loading fails, for example because of a rate limit, an ApiException is thrown.

Downloading files

Items and document attachments can be downloaded:

Examples

Create, print and email a sales invoice

Invoice sales orders (async)

As of October 2026 Exact only supports the async mode of the InvoiceSalesOrders endpoint. Starting the process returns a ProcessID, which you use to fetch the result later.

Rate limits

Exact Online limits the number of API calls per division, per minute and per day. The limits depend on your agreement with Exact. See API limits in Exact's documentation.

After each request, the connection holds the current limits from the response headers:

When you exceed the minutely limit, Exact only sends the minutely headers. The daily values are then 0 until the next minute.

The library can wait automatically when the minutely limit is reached:

The daily limit is not handled automatically. When you reach it, an ApiException with code 429 is thrown. Wait until getDailyLimitReset() before trying again.

Error handling

Every error is thrown as a Picqer\Financials\Exact\ApiException. For HTTP errors, the exception code is the HTTP status code, and the message contains Exact's error message.

If refreshing the tokens fails because the refresh token is no longer valid, you get an ApiException with code 400 and invalid_grant in the message. The user then has to authorize your app again.

Connection problems, like DNS errors and timeouts, are also thrown as ApiException, with code 0. The original exception is available through $e->getPrevious().

To log every request and response, add a Guzzle middleware:

Webhooks

Subscribe to changes with the WebhookSubscription entity:

Check that incoming webhook calls come from Exact with the Authenticatable trait and your webhook secret from the App Center:

Troubleshooting

Error 400: Please add a $select or a $top=1 statement to the query string

Some endpoints require you to limit the fields or the number of records. Select the fields you need, or request one record:

Error 400 when exchanging the authorization code

An authorization code can only be used once. If you exchange the same code again, Exact responds with 400 Bad Request. Exchange the code once, store the tokens, and connect with the stored tokens from then on.

Error 400 with invalid_grant after some time

The refresh token is no longer valid. This usually happens when a new refresh token wasn't stored after a refresh (see Refreshing tokens from multiple processes). The user has to authorize your app again.

A field isn't saved

Check that the field exists in the API reference of the endpoint. The library only sends fields it knows about. If the field is missing from the entity class, please open a pull request.

Server maintenance

Exact Online is unavailable for maintenance every day between 04:00 and 04:30 CET. Avoid scheduling API calls in that window.

Versions and upgrading

Version PHP Guzzle
4.8 and higher 7.4 or higher 7.15.2 or higher
4.0 – 4.7 7.4 or higher 6 or 7
3.x 7.2 or higher 6 or 7
1.x 3

Changes that can affect your code are listed under "Behaviour changes" in the release notes. For example:

Contributing

Pull requests are welcome, especially for missing endpoints and fields. Please add tests for your changes and make sure these pass:

Entities are generated from the Exact Online API reference, using DannyvdSluijs/PicqerExactPhpClientGenerator.

License

MIT. See LICENSE.


All versions of exact-php-client with dependencies

PHP Build Version
Package Version
Requires php Version >=7.4.0
guzzlehttp/guzzle Version ^7.15.2
ext-json Version *
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package picqer/exact-php-client contains the following files

Loading the files please wait ...