தமிழ்நாடு APIMade by Shyam

MADE BY SHYAM · API V1

Tamilnadu API: the complete beginner guide

Created and built by Shyam. You can use this project without signing up, paying, or obtaining an API key.

Quick start: choose what you need

No coding? Open the explorer. Need data for your app? Open the simple results URL and change the year and constituency number.

Browse results →Get one seat as JSON →Download the full year →

1. Start without writing code

Open the website. Choose an election year. Type a constituency's English name or number in the search box, then choose it from the dropdown. Results appear automatically.

The green row is the winning candidate. Other candidates show LOST. NOTA means None of the Above and is a separate voting option. The summary shows the winner, the winning margin, the number of candidates, and the sum of imported votes.

Use Copy result URL to copy the selected seat’s request. Use Open this result as JSON to see the same data in a machine-readable format. Use Download this seat CSV for a spreadsheet of that constituency, or Download year CSV for the entire election. CSV files open in Excel, Google Sheets, or LibreOffice.

If nothing loads, use Try again. If search finds no seat, clear it and choose from the list. English and number search cover all seats. Tamil search covers five Tirunelveli seats from 2011 onward. Source district filters are available for 2021 and 2026.

2. What is an API?

An API is a URL your app can request to obtain structured data. A browser can open these URLs too. GET means read data. JSON is a text format with named fields. CSV is a spreadsheet-friendly table.

Our base URL is https://tamilnadu-api.vercel.app/api/v1. Add an endpoint after it. For example, /elections becomes the election list.

This archive covers Tamil Nadu Assembly general-election cycles, with candidate votes, winners, losers, and party totals. It does not disclose how individual voters voted. Parliamentary elections, local elections, a full by-election history, and current officeholders are not included.

3. Your first API request

  1. Open all supported election years.
  2. Open all 234 constituency summaries for 2026.
  3. Find the constituency_id you want.
  4. Open the complete results for constituency 1 in 2026.

Replace 2026 with a supported year and 1 with an ID from that year's constituency list. Supported years: 1967, 1971, 1977, 1980, 1984, 1989, 1991, 1996, 2001, 2006, 2011, 2016, 2021, 2026. This is an election archive, so years such as 2025 do not have a dataset.

4. Understand the response

Most JSON endpoints return data and meta. List endpoints also include pagination. The API overview and health check have their own top-level fields.

FieldMeaning
dataThe requested object or list of records
meta.dataset_versionThe archive release identifier
meta.release_statusCurrently provisional
meta.official_row_verificationCurrently pending
pagination.pageThe page returned
pagination.limitThe maximum rows on this page
pagination.totalAll matching rows, across every page
pagination.pagesHow many pages to request

A constituency result contains winner, runner_up, winning_margin, candidate_count, nota_votes, counted_vote_sum, quality_flags, and results. Each result row includes these useful fields:

FieldMeaning
idUnique election result record ID; not a permanent person ID
year, constituency_id, constituency_nameElection and seat
candidate_name, partyName and party label from the source
votesImported votes for this row
general_votes, postal_votesVote components when the source provides them; otherwise null
rankRank by candidate votes; ties share rank; NOTA has no candidate rank
statuswon, lost, nota, or unresolved
is_notaTrue for a NOTA voting-option row
vote_share_pctComputed percentage, or null when source totals need review
quality_flagsDetected inconsistencies requiring review
source_id, source_rowWhere the record came from
verification_statusThe record's verification state

null means unavailable or not computed; it does not mean zero. counted_vote_sum is the sum of imported candidate and NOTA rows, not certified turnout. Winning margin is the winner's votes minus the runner-up's votes. Source-derived winners and vote rankings can require review when source data conflicts.

5. Every endpoint

All paths below start after /api/v1. Braces indicate values you replace, not characters you type.

EndpointWhat it returnsQuery parameters
/API name, scope, docs linksNone
/results?year={year}&constituency_id={id}Easy shortcut: full seat summary and every candidate/NOTA rowRequired: year, constituency_id; no pagination
/healthRuntime status and archive countsNone
/electionsAll available years and coverageNone
/elections/{year}One election's coverage and party totalsNone
/elections/{year}/constituenciesSeat summaries, winners, runners-up, marginsq, page, limit
/elections/{year}/constituencies/{id}/resultsEvery imported row for one seat, with summaryNone; no pagination needed
/elections/{year}/resultsPaginated result rows for the yearResult filters, page, limit
/elections/{year}/winnersWinning candidates onlyResult filters, page, limit
/elections/{year}/losersLosing candidates only; excludes NOTAResult filters, page, limit
/elections/{year}/partiesParty votes, shares, contested seats, won seatsNone; returns the full party list
/elections/{year}/export.csvComplete year or filtered rows as CSVResult filters; no pagination
/candidatesSearch result records across yearsResult filters, year, page, limit
/records/{id}One result record using its exact IDNone
/compareTwo elections' results for one modern seatRequired: from, to, constituency_id
/coverageCoverage, unresolved issues, limitationsNone
/sourcesSource references and snapshot checksumsNone

Result filters are q, party, status, and constituency_id. The /candidates search may include NOTA records, so check is_nota when you want only people. /winners and /losers always enforce their respective outcome regardless of other filters.

Outside the API: / and /docs open the explorer, /guide opens this guide, /openapi.json contains the OpenAPI 3.1 specification, and /coverage.json contains the static audit.

6. Search, filters, and useful examples

A question mark starts query parameters. An ampersand joins them: ?year=2021&q=stalin. Parameter names are case-sensitive. Search text and party matching ignore case. q is a substring search across candidate name, seat name, and party on result endpoints; constituency-list search checks seat names.

TaskOpen an example
All 234 winners in 2026Winners
Candidate-name search in 2021Search Stalin
Find Kolathur in 2021Find the seat ID
DMK rows in 2021Exact party label
Only NOTA rows in 2026NOTA
All rows for seat 1 as CSVSeat CSV
Entire 2026 datasetFull CSV
Seat 1 in 2021 versus 2026Compare

party must match a source label exactly, ignoring case. Use the year's /parties endpoint to discover labels; DMK in a historical source and Dravida Munnetra Kazhagam in 2026 are different labels. URL-encode spaces and special characters, or use JavaScript URLSearchParams.

Constituency IDs range from 1 to 234. Boundaries and numbering changed historically. Compare accepts two different election years from 2011 onward only. A changed party label does not necessarily establish a changed party.

7. Get ALL data with pagination

Lists return 50 rows by default, not every record. limit=500 raises the page size to the maximum. If pagination.pages is greater than one, request pages 2, 3, and so on. Keep the same filters on every page. A page beyond the last returns an empty list.

For a complete year in one download, CSV is easiest. For JSON, the following JavaScript fetches every page:

const API_BASE = 'https://tamilnadu-api.vercel.app/api/v1';
async function getAllResults(year) {
  const rows = [];
  let pages = 1;
  for (let page = 1; page <= pages; page++) {
    const response = await fetch(
      `${API_BASE}/elections/${year}/results?page=${page}&limit=500`
    );
    const body = await response.json();
    if (!response.ok) throw new Error(body.error?.message || `HTTP ${response.status}`);
    rows.push(...body.data);
    pages = body.pagination.pages;
  }
  return rows;
}
const rows = await getAllResults(2026);
console.log(rows.length);

To collect all years, first request /elections, then call getAllResults(election.year) for each year. This returns the collected archive; official row verification is still pending.

8. Code you can copy

JavaScript: run this in a modern browser console or in a Node.js 24 .mjs file. Top-level await requires a module or a console that supports it.

const API_BASE = 'https://tamilnadu-api.vercel.app/api/v1';
const response = await fetch(`${API_BASE}/elections/2026/constituencies/1/results`);
const body = await response.json();
if (!response.ok) throw new Error(body.error?.message || `HTTP ${response.status}`);
console.log(body.data.constituency_name);
console.log(body.data.winner?.candidate_name);
console.log(body.data.winning_margin);
console.table(body.data.results);

Python: save as example.py and run python example.py. It uses only the standard library.

import json
import urllib.request
import urllib.error
url = 'https://tamilnadu-api.vercel.app/api/v1/elections/2026/constituencies/1/results'
try:
    with urllib.request.urlopen(url, timeout=30) as response:
        body = json.load(response)
    print(body['data']['constituency_name'])
    print(body['data']['winner']['candidate_name'])
    for row in body['data']['results']:
        print(row['candidate_name'], row['votes'], row['status'])
except urllib.error.HTTPError as error:
    print(error.code, error.read().decode())
except urllib.error.URLError as error:
    print('Connection failed:', error.reason)

Terminal: on Windows PowerShell use curl.exe if curl is an alias for another command.

curl 'https://tamilnadu-api.vercel.app/api/v1/elections'
curl 'https://tamilnadu-api.vercel.app/api/v1/elections/2026/export.csv' -o tamilnadu-2026.csv

Postman: create a request, choose GET, paste a full URL above, and click Send. Leave authentication set to No Auth. Public CORS headers allow browser applications to read the API; no Authorization header is needed.

9. Errors and troubleshooting

HTTP statusMeaningWhat to do
200Request succeededRead data, meta, and any pagination
204OPTIONS preflight succeededNo response body is expected
400Invalid, repeated, or unknown parameterCheck spelling, supported filters, and ranges
404Unsupported election, route, or recordUse /elections and a record ID returned by the API
405Unsupported HTTP methodUse GET, HEAD, or OPTIONS
409Unsafe historical constituency comparisonCompare different supported years from 2011 onward
500Unexpected server errorRetry later; check /health

API errors have error.message. Hosting-level errors can have a different format. HEAD returns headers without a body. POST, PUT, PATCH, and DELETE cannot modify data.

If you get an empty list, check the year, spelling, exact party label, filters, and page number. Query text is limited to 160 characters per value. page starts at 1; limit must be 1–500. CSV accepts filters but does not accept page or limit. Do not send parameters to endpoints that do not list them.

If a seat percentage says Under review, read quality_flags; the API deliberately omits that calculation when source totals conflict. Changing filters cannot verify those source records.

10. Sources, accuracy, and responsible interpretation

This release contains 36,039 result rows across 14 election cycles, including 35,337 candidate rows and 702 NOTA rows. All target years and 234 seat IDs per year are present. That does not certify every candidate row as accurate or complete.

The archive is provisional. Full official ECI row verification remains pending. Historical source copies, OpenCity's 2021 data, and the 2026 source archive are documented in sources. The coverage audit records unresolved issues. API responses retain source references and verification status.

Vote shares are computed from imported candidate votes plus NOTA, not certified turnout. Party shares are null if any seat in that election has relevant quality flags. Matching candidate names across years does not establish identity. Election winners do not necessarily represent current officeholders.

Historical responses use cache headers, so this is not a live counting feed. No commercial uptime guarantee is offered in this release. Use the audit and official election reports when accuracy is critical.

Created and built by Shyam · Independent project, not affiliated with ECI or the Tamil Nadu government. Project credit applies to original work; third-party data and artwork retain their respective rights.

New tools and metadata

Open the API playground to build requests and see JSON, HTTP status, pagination, and JavaScript/Python examples. Try Tirunelveli’s shareable result page: the URL pattern is /read/{year}/{constituency_id}.

The explorer now includes top-five vote bars plus NOTA, source verification details, and year comparison from 2011 onward. Charts use imported vote counts; full candidate rows remain in the table.

/api/v1/elections/2026/districts groups constituency IDs by source district. /api/v1/elections/2026/constituencies?district=Tirunelveli&limit=500 filters a district. Districts are available only for 2021 and 2026 (HTTP 409 otherwise). Nineteen 2026 source labels are missing and appear as null.

Tamil search supports five reviewed Tirunelveli seat names from 2011 onward: /api/v1/elections/2026/constituencies?q=திருநெல்வேலி. It is partial coverage; candidate names remain as imported. Names are sourced from Tirunelveli’s official Tamil election page.

Seat summaries add nullable constituency_name_tamil, district_name, district_source_id, and a tamil_search_aliases array. Source-year districts must not be carried across years. Official candidate-row verification is still pending.