PractiTest REST API: Automating Test Management

PractiTest REST API: Automating Test Management

PractiTest exposes a REST API that lets you create test instances, update execution status, and upload automated test results without touching the UI. This is how most teams integrate their Selenium, Cypress, or Pytest runs with PractiTest -- push results at the end of a CI job instead of manually updating test runs.

Authentication

PractiTest uses HTTP Basic Authentication with your account email and a developer API token.

Generate an API token: go to Account Settings > Developer API Key and click Generate New Key. The token is shown once -- save it immediately.

Every API request requires these headers:

Authorization: Basic <base64(email:token)>
Content-Type: application/json

Base64 encode your credentials:

echo -n "you@company.com:your_api_token" | base64

Or pass credentials directly with curl using -u:

curl -u "you@company.com:your_api_token" \
  https://api.practitest.com/api/v2/projects.json

The base URL for all API calls is https://api.practitest.com/api/v2/.

Finding Project and Test IDs

Before creating anything, get your project ID:

curl -u "you@company.com:your_api_token" \
  "https://api.practitest.com/api/v2/projects.json"

Response:

{
  "data": [
    {
      "id": "4567",
      "type": "projects",
      "attributes": {
        "name": "Mobile App QA",
        "system-id": "PT"
      }
    }
  ]
}

Use that project ID (4567) in subsequent calls. To list tests in a project:

curl -u "you@company.com:your_api_token" \
  "https://api.practitest.com/api/v2/projects/4567/tests.json"

Creating a Test Set via API

curl -u "you@company.com:your_api_token" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "type": "sets",
      "attributes": {
        "name": "CI Run 2026-06-02",
        "description": "Automated results from build #482"
      }
    }
  }' \
  "https://api.practitest.com/api/v2/projects/4567/sets.json"

The response includes the new set's ID, which you need for adding test instances.

Creating Test Instances in a Set

A test instance is a specific test case added to a specific test set. Create one:

curl -u "you@company.com:your_api_token" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "type": "instances",
      "attributes": {
        "set-id": 89012,
        "test-id": 12345
      }
    }
  }' \
  "https://api.practitest.com/api/v2/projects/4567/instances.json"

Uploading Automated Results

This is the core use case for CI integration. Once you have instances created, update their status by posting a run:

curl -u "you@company.com:your_api_token" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "type": "runs",
      "attributes": {
        "instance-id": 67890,
        "status": "passed",
        "run-duration": "00:01:23"
      }
    }
  }' \
  "https://api.practitest.com/api/v2/projects/4567/runs.json"

Valid status values: passed, failed, blocked, no-run.

To include step-level results:

{
  "data": {
    "type": "runs",
    "attributes": {
      "instance-id": 67890,
      "status": "failed",
      "run-duration": "00:00:45",
      "steps": {
        "data": [
          {
            "name": "Navigate to /login",
            "expected-results": "Login form shown",
            "actual-results": "Page returns 500",
            "status": "failed"
          },
          {
            "name": "Enter credentials",
            "expected-results": "Fields accept input",
            "actual-results": "",
            "status": "no-run"
          }
        ]
      }
    }
  }
}

Python Integration Example

A practical pattern for a pytest post-hook that pushes results after each test:

import requests
import base64

PRACTITEST_EMAIL = "you@company.com"
PRACTITEST_TOKEN = "your_api_token"
PROJECT_ID = "4567"
BASE_URL = "https://api.practitest.com/api/v2"

credentials = base64.b64encode(
    f"{PRACTITEST_EMAIL}:{PRACTITEST_TOKEN}".encode()
).decode()

headers = {
    "Authorization": f"Basic {credentials}",
    "Content-Type": "application/json"
}

def push_result(instance_id: int, passed: bool, duration_seconds: int):
    status = "passed" if passed else "failed"
    minutes, seconds = divmod(duration_seconds, 60)
    payload = {
        "data": {
            "type": "runs",
            "attributes": {
                "instance-id": instance_id,
                "status": status,
                "run-duration": f"00:{minutes:02d}:{seconds:02d}"
            }
        }
    }
    resp = requests.post(
        f"{BASE_URL}/projects/{PROJECT_ID}/runs.json",
        json=payload,
        headers=headers
    )
    resp.raise_for_status()
    return resp.json()

CI Pipeline Integration

In a GitHub Actions workflow, add a step after your test suite runs:

- name: Push results to PractiTest
  env:
    PT_EMAIL: ${{ secrets.PRACTITEST_EMAIL }}
    PT_TOKEN: ${{ secrets.PRACTITEST_TOKEN }}
    PT_PROJECT: "4567"
    PT_INSTANCE: "67890"
  run: |
    STATUS="passed"
    if [ ${{ steps.tests.outcome }} != "success" ]; then
      STATUS="failed"
    fi
    curl -u "$PT_EMAIL:$PT_TOKEN" \
      -X POST \
      -H "Content-Type: application/json" \
      -d "{\"data\":{\"type\":\"runs\",\"attributes\":{\"instance-id\":$PT_INSTANCE,\"status\":\"$STATUS\"}}}" \
      "https://api.practitest.com/api/v2/projects/$PT_PROJECT/runs.json"

Rate Limits

PractiTest's API rate limit is 300 requests per minute per account. For large test suites (500+ tests), batch your run creation -- create all instances first, then push results in a loop with a short sleep between requests if you're near the limit.

Next Step

Authenticate with a curl command against /projects.json and confirm you get your project list back. Once that works, write a script that creates a test set, adds three instances, and marks them all passed. That end-to-end flow is the foundation for any CI integration you build on top of it.

Read more

Start now free