Skip to main content
Transform your testing process with: Company-wide Licences, Test Observability & App Percy

Build your own SDK to run Percy tests

A guide to integrating Percy visual testing into the framework of your choice

If Percy currently doesn’t support the test framework you’re using, it’s possible to build your own SDK. Percy works by capturing a snapshot of the DOM in the test browser. From there the DOM is sent to Percy’s API to eventually be rendered concurrently across browsers/widths for screenshots.

Almost all of the heavy lifting is taken care of for you. Percy’s SDKs are thin integrations to the frameworks and are pretty simple under the covers. All SDKs need to:

  • Check if Percy is running
  • Fetch the DOM serialization JavaScript, then execute that JS inside of the test browser
  • Serialize the DOM (by running a JS script in the test browser)
  • POST that DOM & snapshot options (name, widths, etc) to the running Percy server

These steps can change slightly depending on if the SDK you are building is in JavaScript or any other language. If your framework is in JavaScript, skip to this section(you have options).

All Percy SDKs require @percy/cli to process and upload snapshots from the SDK to your Percy project.


Let’s build our own SDK in Python for Selenium. Where this code lives and how it is structured can depend on the framework you’re integrating into. The example we’re going to build here is going to be a generic Selenium Python SDK (framework agnostic), so it will be a function.

For examples sake, we will create a new file called and create a percy_snapshot function:

Copy icon Copy snippet

For this SDK, we’re going to have two required arguments:

  • driver - The Selenium driver (so we can run the JS we need to extract the DOM)
  • name - Snapshot name (required by the Percy API).

We’ll also want to support all of the snapshot options that are available. In Python, that means we can use **kwargs for the rest of these options.

Step 1

The first step to making our SDK work is to check if the local Percy server is running. If it’s not, none of the steps after this should be run (save time/resources).

For snapshots to be captured, processed, and sent to the Percy API, there needs to be a local Percy server running. You can start it by either using percy exec — [test command] or percy exec:start. This is required for Percy to work, so checking if this server is available is a good first step.

The local Percy server provides a /percy/healthcheck endpoint. This endpoint returns info about the server. If this doesn’t respond, we know we can safely exit and not run any more code.

For the sake of the tutorial, we’re going to use the requests package to make HTTP requests. Feel free to use your favorite way of making HTTP requests in the language you’re building in.

Copy icon Copy snippet

Step 2

The second step is to fetch the DOM JavaScript. You can read more about what it does here. After fetching the DOM JavaScript, we can execute it in the browser to make PercyDOM.serialize() available to us in the next step.

Copy icon Copy snippet

Step 3

With the DOM JavaScript library now executed and available in the browser, we can serialize & capture the DOM from the test browser. There are some snapshot options that might alter how the DOM is serialized, so we’ll also pass those along to the JavaScript serialize function.

Copy icon Copy snippet

Step 4

The last step to making this SDK work its to POST that DOM we’ve captured (dom_snapshot) to the locally running Percy server. We’ll also pass along any additional snapshot options that were provided to the percy_snapshot function.

Copy icon Copy snippet

Once the DOM & options are POSTed to the locally running Percy server, you should see a log from the Percy CLI:

Copy icon Copy snippet

Everything from here is taken care of by the Percy CLI. 🎉

Getting Production Ready

The percy_snapshot function we have written so far has been slimmed down to make the example easier to understand. There are a few additional things you should do when writing your own SDK.

We’ll compare the SDK we’ve built here so far to our official Python SDK to highlight the finally polishing needed.

Supplying client & environment user agents

There are two additional keys you should pass along when POST’ing the DOM snapshot to the local Percy server:

  • clientInfo
  • environmentInfo

In our Python SDK, these are the Percy SDK version, Selenium driver version, and Python version:

Copy icon Copy snippet

Then we send that along with the rest of the POST data:

Copy icon Copy snippet

Allow changing the API URL / port

It’s possible for users to change the URL the local Percy server is running on OR the port. This is controlled through an environment variable, PERCY_CLI_API. In our Python SDK, we set a variable which we then interpolate into the network request URLs:

Copy icon Copy snippet

Error handling

Missing from our slimmed example is error handling. You will want to wrap try/catches (or whatever your languages control flow is) to catch errors. The API may also respond with errors, so make sure those errors are raised when received.

Copy icon Copy snippet

Log level control

The Percy CLI logger will respond to various kinds of log level filtering. We make our SDKs follow the same pattern and you can too. We read the PERCY_LOGLEVEL environment variable to determine what to log.

Copy icon Copy snippet

Caching network requests

Another optimization you should make before using this SDK in your workflow is to cache the health check & the DOM JavaScript that’s fetched from the server. For example, in our Python SDK:

Copy icon Copy snippet

This makes is so you’re only requesting these endpoints once per-run of the test suite.

JavaScript based SDKs

Everything discussed above applies to JavaScript based SDKs too. With that said, Percy’s SDK toolchain is built in JavaScript. This means you can consume various packages directly, if you need to.

For example, we have packaged up most of the common tasks up into @percy/sdk-utils. In Puppeteer:

Copy icon Copy snippet

The full Puppeteer SDK is ~42 lines of code (notice the use of @percy/logger):

Copy icon Copy snippet

We're sorry to hear that. Please share your feedback so we can do better

Contact our Support team for immediate help while we work on improving our docs.

We're continuously improving our docs. We'd love to know what you liked

Thank you for your valuable feedback

Is this page helping you?


We're sorry to hear that. Please share your feedback so we can do better

Contact our Support team for immediate help while we work on improving our docs.

We're continuously improving our docs. We'd love to know what you liked

Thank you for your valuable feedback!

Talk to an Expert
Download Copy