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/jsonBase64 encode your credentials:
echo -n "you@company.com:your_api_token" | base64Or pass credentials directly with curl using -u:
curl -u "you@company.com:your_api_token" \
https://api.practitest.com/api/v2/projects.jsonThe 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.