---
title: "Submit Vote"
method: POST
path: "/api/v1/votes"
tags: ["Votes"]
---

# Submit Vote

`POST /api/v1/votes`

## Request

- Base URL: `https://app.aimrank.io` (the document declares no server; this is the origin it was published from)
- URL: `https://app.aimrank.io/api/v1/votes`
- Auth: none declared

Submit a pairwise comparison vote.

Uses atomic transaction with row-level locking to ensure:
- All database changes succeed or fail together
- Concurrent votes on same entities don't cause race conditions

Rate limited to prevent abuse (default: 100/minute).

Tier-cap enforcement (PLATFORM_AUDIT M17): authenticated users have a
monthly-vote limit per their subscription plan. Anonymous voters are
rate-limited at the IP layer (no per-user cap to enforce).

Raises:
    HTTPException 403: Monthly vote limit reached for the user's tier
    HTTPException 404: Ranking or entity not found
    HTTPException 400: Invalid winner_id
    HTTPException 409: Conflict due to concurrent modification
    HTTPException 500: Internal server error

## Request body

- ApiRoutesVotesVoteCreate: Create a pairwise comparison vote.
  - `ranking_id` string, required: UUID of the ranking
  - `entity_a_id` string, required: UUID of first entity
  - `entity_b_id` string, required: UUID of second entity
  - `winner_id` string, nullable: UUID of winner (null = draw/skip)
  - `confidence` number, nullable: User confidence 0-1
  - `decision_time_ms` integer, nullable: Time to decide (max 5 min)
  - `source` string, nullable: Vote source: organic, recommendation, embed, api
  - `invite_code` string, nullable: Invite code that grants access to a non-public ranking. Allows anonymous voting on private rankings when the code's access_type permits it.
  - `idempotency_key` string, nullable: Client-supplied key that makes this vote safely retryable. A retried / double-clicked submission carrying the same key applies the rating delta exactly once — the replay returns the original vote result (200) instead of creating a second vote. Omit for the default fire-and-forget behaviour.
  - `voter_segment` object, nullable: Voter demographics for segment slicing — free-shape JSON object, e.g. {age_group: '25-34', gender: 'female', region: 'EU', role: 'designer'}. Persisted on the vote so leaderboards can be filtered by audience segment (marketing-agency use case). Max 16 keys, value strings ≤ 64 chars.

## Response `201`

Successful Response

- unknown

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/aimrank/apis/aimrank-api.md) · [All operations](https://skmtc.dev/aimrank/apis/aimrank-api/llms.txt) · [OpenAPI document](https://skmtc.dev/aimrank/apis/aimrank-api/revisions/3574d0aff459?raw)
