GitHub ↗

API Reference

All endpoints are JSON. Authentication uses a JWT bearer token returned by /auth/register and /auth/login. Interactive docs are available at /docs (Swagger UI) when the server is running.

Base URL

http://localhost:8000

Authentication

POST /auth/register

Create an account and receive a JWT.

Request body

{ "email": "you@example.com", "password": "hunter2" }

Responses

Status Body
200 { "access_token": "<jwt>", "token_type": "bearer" }
400 { "detail": "Email already registered" }

POST /auth/login

Authenticate and receive a JWT.

Request body

{ "email": "you@example.com", "password": "hunter2" }

Responses

Status Body
200 { "access_token": "<jwt>", "token_type": "bearer" }
401 { "detail": "Incorrect email or password" }

GET /auth/me

Return the authenticated user's profile.

Headers

Authorization: Bearer <jwt>

Responses

Status Body
200 { "id": 1, "email": "you@example.com" }
401 { "detail": "Not authenticated" }

URLs

All /urls routes except stats and redirect require a JWT bearer header.

POST /urls/shorten

Create a short URL.

Request — query parameter target_url (a bare scalar in the FastAPI handler, so it is a query parameter, not a JSON body):

POST /urls/shorten?target_url=https://example.com/very/long/path
curl -s -X POST 'http://localhost:8000/urls/shorten?target_url=https://example.com/very/long/path' \
  -H 'Authorization: Bearer <jwt>'

Response 200

{
  "id": 12,
  "short_code": "Ab3xY9",
  "target_url": "https://example.com/very/long/path",
  "clicks": 0,
  "is_active": true
}

GET /urls/my

List the authenticated user's URLs, newest first.

Response 200

[
  {
    "id": 12,
    "short_code": "Ab3xY9",
    "target_url": "https://example.com/very/long/path",
    "clicks": 3,
    "is_active": true
  }
]

GET /urls/{short_code}/stats

Public click statistics for any short code.

Response 200

{
  "short_code": "Ab3xY9",
  "target_url": "https://example.com/very/long/path",
  "clicks": 3,
  "total": 3
}
Status Body
404 { "detail": "Not Found" }

DELETE /urls/{short_code}

Delete a URL. Owner-scoped — only the user who created the URL can delete it. Returns 204 No Content on success.

Status Detail
204 Deleted
404 URL not found or not owned by you

GET /urls/r/{short_code}

Redirect to the target URL and increment the click counter.

Responses

Status Detail
302 Redirect to target_url
404 { "detail": "URL not found" } (inactive URLs are ignored)

Only active URLs redirect — is_active = true is required.

System

GET /health

Liveness probe.

{ "status": "ok" }

Data model

User — id, email (unique), password_hash

URL — id, short_code (unique), target_url, owner_id (FK → User), clicks (int, default 0), is_active (bool), created_at

Error format

FastAPI errors follow the standard shape:

{ "detail": "Human-readable message" }