API reference

For LLMs

Every field configurable in the dashboard is configurable here — same schema, same source of truth.

Base URL: https://gitfile.dev60 requests / minute / keyX-RateLimit-* headers on every response

Introduction

The Gitfile API reads and writes the same banner configuration the dashboard does, and serves the public banner image — everything over HTTPS. Every request and response body is JSON, except the banner endpoint, which returns the rendered image directly.

Authentication

Most endpoints accept either credential — whichever is present on the request:

  • An API key — Authorization: Bearer gtf_live_..., generated from Dashboard → API — the only place a key can ever be created. Each key carries one or more scopes (config:read, config:write) and can only ever act on its own account's configuration.
  • The dashboard's session cookie — what the dashboard itself uses; useful if you're scripting against your own logged-in browser session.

There is no endpoint to create, list, or revoke API keys — that's deliberate. Keys are managed exclusively from an authenticated dashboard session, never over the API itself, so a leaked key can never mint or delete other credentials. The public banner endpoint needs no auth at all.

Rate limits

Authenticated requests are limited to 60 requests/minute per API key; the public banner endpoint is limited to 300 requests/minute per client IP. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers — back off once Remaining hits zero rather than waiting for a 429.

Configuration

GET/api/v1/config

Retrieve the current configuration

Returns the authenticated user's current banner configuration as JSON.

Auth: Session cookie, or an API key with the config:read scope

Returns

The full banner configuration.

GET/api/v1/config
curl "https://gitfile.dev/api/v1/config" \
  -H "Authorization: Bearer gtf_live_xxxxxxxxxxxx"
200Response

The full banner configuration.

{
  "displayName": "Ada Lovelace",
  "bio": "Building things that compile on the first try (eventually).",
  "showAvatar": true,
  "customAvatarUrl": null,
  "appearance": {
    "theme": "dark",
    "accentColor": "#0A84FF",
    "layout": "wide-horizontal",
    "typography": "system",
    "style": "cards",
    "radius": 20
  },
  "showFields": {
    "repositories": true,
    "followers": true,
    "stars": true,
    "contributionStreak": true,
    "commits": false,
    "pullRequests": false,
    "issues": false,
    "topLanguages": true,
    "pinnedRepos": [
      "gitfile",
      "dotfiles"
    ],
    "organization": false,
    "location": false,
    "proBadge": false,
    "contributionGraph": false
  },
  "profilePage": {
    "showLiveBanner": true,
    "showReadme": true,
    "showLanguageBreakdown": true,
    "showPinnedRepos": true,
    "showRecentActivity": true,
    "showStatsGrid": true,
    "showContributionHeatmap": true
  },
  "public": true,
  "includePrivateRepos": false
}
PUT/api/v1/config

Replace the configuration

Replaces the full configuration in one call — every field below is required, same as saving the entire dashboard form at once.

Auth: Session cookie, or an API key with the config:write scope

Request body

All fields required — this is a full replace, not a merge. Use PATCH below to change just a few fields.

displayNamestring

1–80 characters. The banner's headline name.

biostring

Up to 240 characters, shown under the display name.

showAvatarboolean

Whether the GitHub avatar is rendered.

customAvatarUrlstring | null

Overrides the GitHub avatar with a custom image URL, or null to use it.

appearanceobject

Visual style of the banner.

showFieldsobject

Which stats and fields are visible on the banner.

profilePageobject

Which sections are visible on the public /{username} profile page.

publicboolean

Whether the banner URL and the /{username} page render real data or a private placeholder.

includePrivateReposboolean

Opt-in to include private/org repos in aggregate stats. Requires a connected token with repo scope — this flag alone does nothing without one.

Returns

The configuration as saved.

PUT/api/v1/config
curl -X PUT "https://gitfile.dev/api/v1/config" \
  -H "Authorization: Bearer gtf_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"theme":"dark","accentColor":"#0A84FF"}'
200Response

The configuration as saved.

{
  "displayName": "Ada Lovelace",
  "bio": "Building things that compile on the first try (eventually).",
  "showAvatar": true,
  "customAvatarUrl": null,
  "appearance": {
    "theme": "dark",
    "accentColor": "#0A84FF",
    "layout": "wide-horizontal",
    "typography": "system",
    "style": "cards",
    "radius": 20
  },
  "showFields": {
    "repositories": true,
    "followers": true,
    "stars": true,
    "contributionStreak": true,
    "commits": false,
    "pullRequests": false,
    "issues": false,
    "topLanguages": true,
    "pinnedRepos": [
      "gitfile",
      "dotfiles"
    ],
    "organization": false,
    "location": false,
    "proBadge": false,
    "contributionGraph": false
  },
  "profilePage": {
    "showLiveBanner": true,
    "showReadme": true,
    "showLanguageBreakdown": true,
    "showPinnedRepos": true,
    "showRecentActivity": true,
    "showStatsGrid": true,
    "showContributionHeatmap": true
  },
  "public": true,
  "includePrivateRepos": false
}
PATCH/api/v1/config

Update part of the configuration

Partially updates specific fields without resending the whole object — nested objects (appearance, showFields) are shallow-merged too.

Auth: Session cookie, or an API key with the config:write scope

Request body

Every field is optional — send only what you want to change.

displayNamestring

1–80 characters. The banner's headline name.

biostring

Up to 240 characters, shown under the display name.

showAvatarboolean

Whether the GitHub avatar is rendered.

customAvatarUrlstring | null

Overrides the GitHub avatar with a custom image URL, or null to use it.

appearanceobject

Visual style of the banner.

showFieldsobject

Which stats and fields are visible on the banner.

profilePageobject

Which sections are visible on the public /{username} profile page.

publicboolean

Whether the banner URL and the /{username} page render real data or a private placeholder.

includePrivateReposboolean

Opt-in to include private/org repos in aggregate stats. Requires a connected token with repo scope — this flag alone does nothing without one.

Returns

The full configuration after the patch is applied.

PATCH/api/v1/config
curl -X PATCH "https://gitfile.dev/api/v1/config" \
  -H "Authorization: Bearer gtf_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"showFields":{"contributionStreak":false}}'
200Response

The full configuration after the patch is applied.

{
  "displayName": "Ada Lovelace",
  "bio": "Building things that compile on the first try (eventually).",
  "showAvatar": true,
  "customAvatarUrl": null,
  "appearance": {
    "theme": "dark",
    "accentColor": "#0A84FF",
    "layout": "wide-horizontal",
    "typography": "system",
    "style": "cards",
    "radius": 20
  },
  "showFields": {
    "repositories": true,
    "followers": true,
    "stars": true,
    "contributionStreak": true,
    "commits": false,
    "pullRequests": false,
    "issues": false,
    "topLanguages": true,
    "pinnedRepos": [
      "gitfile",
      "dotfiles"
    ],
    "organization": false,
    "location": false,
    "proBadge": false,
    "contributionGraph": false
  },
  "profilePage": {
    "showLiveBanner": true,
    "showReadme": true,
    "showLanguageBreakdown": true,
    "showPinnedRepos": true,
    "showRecentActivity": true,
    "showStatsGrid": true,
    "showContributionHeatmap": true
  },
  "public": true,
  "includePrivateRepos": false
}
POST/api/v1/config/regenerate

Force a cache refresh

Forces an immediate cache invalidation of the public banner image (bypassing the normal ~24h refresh schedule). No request body.

Auth: Session cookie, or an API key with the config:write scope

Returns

Confirms the banner cache was invalidated.

POST/api/v1/config/regenerate
curl -X POST "https://gitfile.dev/api/v1/config/regenerate" \
  -H "Authorization: Bearer gtf_live_xxxxxxxxxxxx"
200Response

Confirms the banner cache was invalidated.

{
  "status": "regenerating",
  "username": "octocat"
}
DELETE/api/v1/account

Delete your account

Permanently deletes the signed-in account: saved configuration, linked identities, username claims, API keys and cached stats, and revokes the OAuth grants on GitHub/GitLab (best effort). The body must carry one of your linked usernames as a typed confirmation. Session-only: an API key cannot call this endpoint. Not reversible.

Auth: Session cookie only (API keys are rejected)

Returns

Confirms the account was deleted and the session cookie cleared.

DELETE/api/v1/account
curl -X DELETE "https://gitfile.dev/api/v1/account" \
  -H "Content-Type: application/json" \
  -b "gitfile_session=..." \
  -d '{"confirm":"octocat"}'
200Response

Confirms the account was deleted and the session cookie cleared.

{
  "ok": true
}

Banner

GET/api/v1/banner/{username}

Get the rendered banner image

Public endpoint, no auth required — returns the rendered banner directly (not JSON), as SVG, PNG, or plain ANSI text for a terminal. Style and which stats are shown can be overridden per-request with query params, without touching the saved config. Called from curl, Wget, or HTTPie with no explicit format, it returns ansi text automatically. The short gitfile.dev/{username} path (no /api/v1/banner/ prefix) works the same way for a CLI caller — it's the same path a browser uses for the public profile page, content-negotiated by User-Agent. When the same name exists on both GitHub and GitLab (or was claimed first on the other provider), add the provider: /api/v1/banner/github/{username} or /api/v1/banner/gitlab/{username} (and gitfile.dev/github/{username} / gitfile.dev/gitlab/{username} for the profile page and curl).

Auth: None — public

Path parameters

usernamestringrequired

GitHub username (case-insensitive). Accepts a .svg, .png, or .txt suffix as an alternative to ?format=.

Query parameters

formatstring

"svg" (default for browsers), "png", or "ansi" (default automatically when the request comes from curl, Wget, or HTTPie — see the .txt suffix / Terminal usage below).

themestring

"light", "dark", "auto", or "custom".

accentColorstring

A 6-digit hex color, e.g. "#0A84FF".

layoutstring

"compact", "wide-horizontal", or "vertical-card".

typographystring

"system", "mono", or "editorial".

stylestring

"cards", "minimal", "split", or "terminal".

radiusnumber

Corner radius in px, 0–32.

repositoriesboolean

Show the repository count. (true/false or 1/0)

followersboolean

Show the follower count. (true/false or 1/0)

starsboolean

Show total stars earned. (true/false or 1/0)

contributionStreakboolean

Show the current day streak. (true/false or 1/0)

commitsboolean

Show commits in the last 30 days. (true/false or 1/0)

pullRequestsboolean

Show pull requests in the last 30 days. (true/false or 1/0)

issuesboolean

Show issues in the last 30 days. (true/false or 1/0)

topLanguagesboolean

Show the top-languages breakdown. (true/false or 1/0)

organizationboolean

Show the GitHub organization/company field. (true/false or 1/0)

locationboolean

Show the location field. (true/false or 1/0)

proBadgeboolean

Show the "Pro" badge. (true/false or 1/0)

contributionGraphboolean

Show the contribution heatmap. (true/false or 1/0)

pinnedReposstring

Comma-separated repo names, up to 3 (e.g. pinnedRepos=gitfile,dotfiles).

colsnumber

ansi format only. Terminal width in columns (20-400, default 100). curl can't detect this itself — pass cols=$(tput cols) from your shell.

linesnumber

ansi format only. Terminal height in rows (5-200, default 30). Same idea as cols — pass lines=$(tput lines).

colorstring

"truecolor" (default), "256", or "none" for plain, uncolored text.

Returns

The rendered banner. Content-Type is image/svg+xml (default for browsers), image/png with ?format=png, or text/plain (ANSI escape codes included) for ?format=ansi / a CLI caller.

GET/api/v1/banner/{username}
# The short form works too (gitfile.dev/{username}) — curl auto-detects
# a perfect fit:
curl "https://gitfile.dev/octocat?cols=$(tput cols)&lines=$(tput lines)"

# Restyle it the same way you would the SVG/PNG:
curl "https://gitfile.dev/octocat?style=terminal&theme=dark&accentColor=%2330D158"

# Force a specific format:
curl "https://gitfile.dev/api/v1/banner/octocat.svg?theme=light&accentColor=%230A84FF&style=minimal&stars=false"
200Response

The rendered banner. Content-Type is image/svg+xml (default for browsers), image/png with ?format=png, or text/plain (ANSI escape codes included) for ?format=ansi / a CLI caller.

<svg width="1280" height="360" viewBox="0 0 1280 360" ...>
  <!-- rendered banner markup -->
</svg>

Errors

Every non-2xx response, from any endpoint above, shares the same JSON envelope: { error, message }, plus issues on a 400. The status badges under each endpoint's "Returns" link back here.

400invalid_request

The request body or query string failed validation — see `issues` for exactly which field and why.

{
  "error": "invalid_request",
  "message": "Invalid payload",
  "issues": [
    { "path": ["appearance", "accentColor"], "message": "accentColor must be a 6-digit hex color" }
  ]
}
401unauthenticated / api_auth_error

No session cookie and no Authorization header were provided, or the API key wasn't recognized (unknown, malformed, or revoked).

{
  "error": "api_auth_error",
  "message": "Missing or invalid Authorization: Bearer gtf_live_... header"
}
403api_auth_error

The API key is valid but doesn't have the scope this endpoint requires (e.g. a `config:read`-only key calling a `config:write` route).

{
  "error": "api_auth_error",
  "message": "This API key does not have the \"config:write\" scope"
}
429api_auth_error

More than 60 requests/minute for this API key (or 300/minute per client IP on the public banner endpoint). Every response carries `X-RateLimit-*` headers so you can back off before hitting this.

{
  "error": "api_auth_error",
  "message": "Rate limit exceeded (60 requests/minute)"
}
500internal_error

Something went wrong on our end. Safe to retry with backoff.

{
  "error": "internal_error",
  "message": "Something went wrong"
}