Overview

The Right Steps Victory Election Analytics API provides programmatic access to cross-voting analysis data. Use it to query how voters who supported a specific candidate in one race voted in other races on the same ballot.

The API is read-only and returns JSON responses. All endpoints require authentication via an API key.

Getting an API Key: Contact your Super Admin or Campaign Manager to generate an API key for your account. Keys are managed within the Election Analytics section of the platform.

Authentication

All API requests must include a valid API key in the X-API-Key HTTP header:

X-API-Key: your_api_key_here

API keys are SHA-256 hashed on the server. Only the first 8 characters (prefix) are stored in plaintext for identification. Keep your full key secure—it cannot be recovered if lost.

Keys can be deactivated or expired by an administrator at any time. Usage is tracked per request.

Base URL

https://victory.hardingps.com/api/v1/election

All endpoint paths below are relative to this base URL.

Endpoints

GET

List Datasets

/datasets

Returns all completed election datasets available for analysis.

Response:

{
  "datasets": [
    {
      "id": 1,
      "name": "2024 Primary Election",
      "description": "Primary election CVR data",
      "election_date": "2024-03-19",
      "election_type": "primary",
      "total_records": 150000,
      "status": "completed"
    }
  ]
}
GET

Get Dataset Contests

/datasets/{dataset_id}/contests

Returns all contests and their choices for a specific dataset. Use this to discover valid contest and choice IDs for cross-voting queries.

ParameterTypeInDescription
dataset_idintegerpathThe ID of the dataset

Response:

{
  "dataset_id": 1,
  "dataset_name": "2024 Primary Election",
  "contests": [
    {
      "id": 1,
      "name": "Presidential Race",
      "position": 0,
      "choices": [
        {"id": 1, "name": "Candidate A", "vote_count": 50000},
        {"id": 2, "name": "Candidate B", "vote_count": 45000}
      ]
    }
  ]
}
GET

Cross-Voting Analysis

/datasets/{dataset_id}/analysis/cross-voting

Analyzes how voters who chose specific candidates in source races voted in target comparison races. Supports querying multiple source contest/choice pairs in a single request.

ParameterTypeInRequiredDescription
dataset_idintegerpathYesThe ID of the dataset
source_contest_idsstringqueryYes*Comma-separated source contest IDs (e.g., 1,2,3)
source_choice_idsstringqueryYes*Comma-separated source choice IDs matching each contest (e.g., 10,20,30)
compare_contest_idsstringqueryYesComma-separated target contest IDs to compare against (e.g., 4,5)

*Legacy parameters primary_contest_id and primary_choice_id are also accepted for single-source requests.

Single-source response:

{
  "dataset_id": 1,
  "dataset_name": "2024 Primary Election",
  "primary_contest": {"id": 1, "name": "Presidential Race"},
  "primary_choice": {"id": 10, "name": "Candidate A"},
  "total_matching_voters": 50000,
  "comparisons": [
    {
      "contest_id": 4,
      "contest_name": "Senate Race",
      "distribution": [
        {"choice": "Senator X", "count": 30000, "percentage": 60.0},
        {"choice": "Senator Y", "count": 18000, "percentage": 36.0},
        {"choice": "UNDERVOTE", "count": 2000, "percentage": 4.0}
      ]
    }
  ]
}

Multi-source response:

{
  "dataset_id": 1,
  "dataset_name": "2024 Primary Election",
  "results": [
    {
      "source_contest": {"id": 1, "name": "Presidential Race"},
      "source_choice": {"id": 10, "name": "Candidate A"},
      "total_matching_voters": 50000,
      "comparisons": [
        {
          "contest_id": 4,
          "contest_name": "Senate Race",
          "distribution": [
            {"choice": "Senator X", "count": 30000, "percentage": 60.0}
          ]
        }
      ]
    }
  ]
}

Code Examples

# List all datasets
curl -H "X-API-Key: your_api_key" \
  https://victory.hardingps.com/api/v1/election/datasets

# Get contests for a dataset
curl -H "X-API-Key: your_api_key" \
  https://victory.hardingps.com/api/v1/election/datasets/1/contests

# Cross-voting analysis (single source)
curl -H "X-API-Key: your_api_key" \
  "https://victory.hardingps.com/api/v1/election/datasets/1/analysis/cross-voting?\
source_contest_ids=1&source_choice_ids=10&compare_contest_ids=4,5"

# Cross-voting analysis (multiple sources)
curl -H "X-API-Key: your_api_key" \
  "https://victory.hardingps.com/api/v1/election/datasets/1/analysis/cross-voting?\
source_contest_ids=1,2&source_choice_ids=10,20&compare_contest_ids=4,5"
import requests

API_KEY = "your_api_key"
BASE_URL = "https://victory.hardingps.com/api/v1/election"
headers = {"X-API-Key": API_KEY}

# List datasets
response = requests.get(f"{BASE_URL}/datasets", headers=headers)
datasets = response.json()

# Get contests for a dataset
dataset_id = 1
response = requests.get(
    f"{BASE_URL}/datasets/{dataset_id}/contests",
    headers=headers
)
contests = response.json()

# Cross-voting analysis
params = {
    "source_contest_ids": "1",
    "source_choice_ids": "10",
    "compare_contest_ids": "4,5"
}
response = requests.get(
    f"{BASE_URL}/datasets/{dataset_id}/analysis/cross-voting",
    headers=headers,
    params=params
)
analysis = response.json()
print(analysis)
const API_KEY = "your_api_key";
const BASE_URL = "https://victory.hardingps.com/api/v1/election";
const headers = { "X-API-Key": API_KEY };

// List datasets
const datasets = await fetch(`${BASE_URL}/datasets`, { headers })
  .then(res => res.json());

// Get contests
const datasetId = 1;
const contests = await fetch(
  `${BASE_URL}/datasets/${datasetId}/contests`, { headers }
).then(res => res.json());

// Cross-voting analysis
const params = new URLSearchParams({
  source_contest_ids: "1",
  source_choice_ids: "10",
  compare_contest_ids: "4,5"
});
const analysis = await fetch(
  `${BASE_URL}/datasets/${datasetId}/analysis/cross-voting?${params}`,
  { headers }
).then(res => res.json());

console.log(analysis);

Error Handling

The API uses standard HTTP status codes. Errors return a JSON body with error and message fields.

CodeMeaningCommon Cause
200SuccessRequest completed successfully
400Bad RequestMissing or invalid query parameters
401UnauthorizedMissing, invalid, or expired API key
404Not FoundDataset or contest ID does not exist
500Server ErrorUnexpected internal error

Error response format:

{
  "error": "Bad Request",
  "message": "Missing required parameter: source_contest_ids"
}