Documentation navigation
GET /v2/developers/summaries

AI summaries of developers and their repositories

Access
Authenticated
search:read
Cost
Free
Rate limit
30 / minute per account
Quota
None

The AI-written plain-language summaries for developer ids you already hold: ai.person_summary, a three-line pitch (who they are across their career, then the range of what they build and their most notable work, reading their LinkedIn, projects and full GitHub history) and ai.repo_summaries (a short developer-friendly blurb per key repository). Each row is the SAME developer object the other routes return, with the two summary facets in its details object — no parallel shape invented. The value is cached across accounts. A developer whose summary is not yet computed comes back pending (the job is dispatched); read the same id again to collect the finished ok value. The first request for a developer is charged for the developer and for the summaries. While a summary your account has paid for is still being written, reading it again on this call is free, and so is the one read that returns it finished. Every later read of a finished summary is charged for the developer again, and never again for the summaries themselves while your purchase is current. A summary that is already written when you first ask is returned at once and charged as a first request. If a summary cannot be written, the charge for the summaries is returned and a later read requests it afresh. A purchase lapses after a while, and the first read after that is charged as a first request.

Summaries are NOT a depth tier — depth is how far out a search reaches; this is a separate call, like emails.

Query parameters

  • ids string

    Developer ids, comma separated, at most 25. A developer's id is GitHub's public NUMERIC user id, stringified — deterministic, rename-proof, and exactly what `developer.developerId` carries on every response from this API, so ids from search paste straight in. Only have usernames? Use `logins` instead (mixing both is fine). Repeating an id changes nothing and costs nothing: ids are deduplicated before we fetch or bill.

    max 1625 chars
  • logins string

    GitHub usernames, comma separated, at most 25 — the friendly alternative to `ids`, freely mixable with it. Logins resolve to the same developers their numeric ids name; a login that resolves to nobody is omitted from the results and costs nothing, the same contract an unknown id has. Prefer `ids` when you carry results between calls: a login can be renamed and then belongs to someone else.

    max 1625 chars

Responses

  • 200 The developers we hold, each with its AI summary facets
    • results array of PublicDeveloper required

      The developers we hold data for, in the same shape a search result carries. Ids we hold nothing for are omitted rather than returned empty — and are not billed.

      max 250 items
      • id string required

        The stable GitHub numeric id for this developer. Immutable (unlike the login) — this is the handle to pass to every other endpoint.

        max 64 chars
      • login string required

        The GitHub handle as of the last snapshot. Display it, do not key on it: handles are renameable and reusable.

        max 200 chars
      • name string | null required

        Display name as the developer set it on GitHub. Null when they set none.

        max 200 chars
      • avatarUrl string | null required

        GitHub avatar image URL. Null when absent.

        max 2048 chars
      • hasEmail boolean required

        Whether we hold at least one email address for this developer. It says we have an address on file, not that the address works — we do not check deliverability. The addresses themselves are never returned by a search or a base profile read: this boolean is the free signal, and the paid `contact.emails` facet is the only way to get them. Use `hasEmail` on a search request to filter to developers we hold an address for, without paying.

      • hasLinkedin boolean required

        Whether we hold a LinkedIn match for this developer — i.e. whether `enriched` will return `identity.*` and `contact.socials` for them. Available on the same base row as `hasEmail`: read both to decide what depth is worth requesting.

      • hasLocation boolean required

        Whether we hold any resolved location for this developer (from LinkedIn or a geocoded GitHub string). A free presence flag alongside `hasEmail`/`hasLinkedin`. The resolved `location` OBJECT rides `details.contact.location` at `enriched` (like the LinkedIn URL), not on the spine. Filter on it with `requireLocation=true`.

      • details PublicDetails required

        Every detail about this developer beyond the identity spine, as a KEYED object nested by namespace. A namespace and a member appear only when resolved; absent details are omitted entirely. The set grows with depth: `core` fills `githubProfile.*` and `score.cracked`; `enriched` adds `identity.*`, `contact.socials` and `contact.location`; `deep` adds `github.garden`. À-la-carte: `tags.repos` folds onto `githubProfile.repos[]`, the emails route fills `contact.emails`.

        • githubProfile object

          The free GitHub profile: `githubProfile.core` (bio, org, languages, snapshot counts) and `githubProfile.repos`. Named for its source — everything here comes straight from GitHub.

          • core object

            bio, currentRole, organization, university, languages, joinedAt, followers, following, totalStars.

          • repos array of ProfileRepoWithTags

            Top repositories (default 4), with `owned`/`commits` and, when resolved, `subjects`/`technologies` (`tags.repos`) and a plain-English `summary`/`themes` (`ai.repo_summaries`) folded on. The AI blurbs live HERE, on the repos shown, not in a separate list.

            • name string required

              The repository name alone, without the owner (`linux`).

              max 200 chars
            • fullName string required

              GitHub `owner/name`. This is the join key: an evidence repo and a profile repo describing the same repository carry the same `fullName`.

              max 200 chars
            • description string | null required

              The repository's own GitHub description, verbatim. Null when it has none.

              max 5000 chars
            • language string | null required

              GitHub's primary language for the repository. Null when GitHub reports none (an empty or docs-only repo); never inferred from the code.

              max 200 chars
            • stars integer required

              Stargazer count at the time this profile was snapshotted, not at request time.

              min 0, max 100000000
            • commits integer

              How many commits THIS developer authored to this repository over the trailing year. This is the difference between a project they wrote and a project they landed one drive-by fix in — a 385k-star repository with 4 of their commits is not their work. Absent when no commit signal was captured; 0 means none in the trailing year, which for older dormant work is not the same as none ever.

              min 0, max 100000000
            • owned boolean

              True when the repository sits under the developer's own account, false when it is someone else's (an organisation's or another person's) and they are a contributor to it. This is the difference between "they built this" and "they committed to this", and it is the ONLY honest basis for crediting a repository's stars to a person. Absent when no ownership signal was captured; treat absent as unknown, never as false.

            • url string required

              The public github.com URL of the repository.

              max 2048 chars
            • subjects array of string

              Subject/domain tags for this repository. Present only when `tags.repos` was resolved and this repo carried tags.

            • technologies array of string

              Technology tags for this repository. Present only when `tags.repos` was resolved and this repo carried tags.

            • summary string

              A one-line, plain-English blurb on what this repository is, written for a non-technical reader. Present only when `ai.repo_summaries` was resolved and the model could describe this repo. The summaries are folded ONTO the repos you see here — there is no separate list — so what is described is exactly what is shown.

            • themes array of string

              A few short themes the AI drew from this repository. Present alongside `summary` when `ai.repo_summaries` was resolved.

        • score object

          The cracked score (`score.cracked`).

          • cracked object

            crackedScore, tier.

        • identity object

          The resolved external identity (`enriched`): `identity.linkedin`, `identity.experience`, `identity.education`.

          • linkedin object

            The resolved LinkedIn identity scalars (linkedin, headline, title, company, seniority, expertise, location).

          • experience array of object

            Employment history, newest first. Present only when non-empty.

            • title string | null

              Job title.

              max 200 chars
            • company string | null

              Employer name.

              max 200 chars
            • startDate string | null

              Start date exactly as the source rendered it. Free text, not normalised.

              max 200 chars
            • endDate string | null

              End date, same unnormalised free-text form. Null on a current role.

              max 200 chars
            • current boolean | null

              The source's own "still there" flag.

          • education array of object

            Education history, in the order the source listed it. Present only when non-empty. Entries are not ordered by level and can include secondary schools, so read every entry when confirming a university or a graduation year.

            • school string | null

              Institution name.

              max 200 chars
            • degree string | null

              Degree awarded.

              max 200 chars
            • fieldOfStudy string | null

              Field of study.

              max 200 chars
            • startDate string | null

              Start date as the source rendered it.

              max 200 chars
            • endDate string | null

              End date as the source rendered it.

              max 200 chars
        • contact object

          Contact channels: `contact.socials` (enriched), `contact.location` (enriched), `contact.emails` (the emails route).

          • socials object

            github, linkedin social links.

          • emails array of string

            Every address we observe for this developer. Present only when we hold at least one, resolved via the emails route.

          • location object | null

            Where this developer is, resolved once and LinkedIn-preferred: `source` says whether the clean city/country came from their LinkedIn (`linkedin`) or from geocoding their GitHub location string (`github`). Present at `enriched`+ whenever `hasLocation` is true — the spine carries only the `hasLocation` flag. Note the GitHub geocode is rough (it often drops a country string into `city`); trust `source: "linkedin"` over `"github"`.

            • raw string | null required

              The location string exactly as the developer typed it on GitHub ("SF / remote").

              max 200 chars
            • city string | null required

              City resolved from `raw` by geocoding. Null when `raw` was empty or unresolvable.

              max 200 chars
            • state string | null

              The country subdivision (US state, province, region) as a full name, e.g. "Ohio". Present only from the clean LinkedIn overlay, for the developers we hold a match for. Absent otherwise: the GitHub geocoder does not reliably resolve it (so we do not surface its guess), and it is legitimately absent for city-states and metro-only profiles.

              max 200 chars
            • country string | null required

              Country resolved from `raw`. Null when `raw` was empty or unresolvable. It can also be null while `city` is set, so read `city` and `raw` beside it before treating a location as unknown.

              max 200 chars
            • source string

              Where the resolved city/country came from: `linkedin` when our LinkedIn overlay supplied it (clean, self-reported on LinkedIn), `github` when it is geocoded from the GitHub `raw` string. Most developers have no LinkedIn match, so `github` is the common case and its city/country are often sparse. Absent on unresolved payloads.

              linkedingithub
        • github object

          The live-GitHub block (`deep`): `github.gardenSummary` by default, or the full `github.garden` with `garden=full`.

          • garden object

            The full contribution heatmap: every day’s count for the trailing year. Only with `garden=full`.

          • gardenSummary GardenSummary

            The contribution garden as a compact read (the default `garden=summary`): totals, active days and weeks, the last 90 days, per-month counts.

            • from string required

              First day of the garden window, `YYYY-MM-DD`, inclusive.

            • to string required

              Last day of the garden window, `YYYY-MM-DD`, inclusive.

            • total integer required

              Public contributions across the window.

            • activeDays integer required

              Days in the window with at least one contribution.

            • activeWeeks integer required

              Distinct weeks in the window with at least one contribution. Steady contributors score close to 52.

            • last90Days integer required

              Contributions in the 90 days ending on `to`.

            • lastActiveDay string | null required

              The most recent day with a contribution, `YYYY-MM-DD`. Null when the window is empty.

            • byMonth object required

              Contributions per calendar month, `YYYY-MM` → count. Months with none are omitted.

            • fetchedAt string required

              When the underlying garden was read from GitHub, as an ISO 8601 instant.

            • followers integer | null required

              Current GitHub follower count, live. Null on a garden cached before live stats existed.

            • totalStars integer | null required

              Stars on repositories they OWN, live. Null on a garden cached before live stats existed.

        • ai object

          À-la-carte AI prose. `person_summary`/`repo_summaries` come from the `/developers/summaries` route or `facets=ai.person_summary,ai.repo_summaries` on any route. `match_rationale` comes from `facets=ai.match_rationale` on SEARCH — it is judged against your `q`. Compute is async: each member carries a `status` — `ok` or `pending` (with `retryAfterMs`: wait that long, then re-fetch this call once — the value is computed once and cached, so the retry returns fast). A pending `person_summary`/`repo_summaries` can also be collected from `/developers/summaries` with the same ids: there, reading a developer is free while a summary your account has paid for is still being written, and for the one read that returns it finished. On `ok`, `person_summary` and `match_rationale` carry their `value` here; `repo_summaries` instead folds its blurbs onto `githubProfile.repos[]` and carries status only. Read status before value.

          • person_summary object

            A three-line pitch for this developer, as one string of up to three lines, each opening with a bullet: who they are across their career, then the range of what they build and their most notable work.

            • status string required
              okpending
            • value object
            • retryAfterMs number

              On `pending` only: milliseconds to wait before re-fetching this call. The value is compute-once/cached, so the retry returns `ok` fast once it lands.

          • repo_summaries object

            Poll marker for the per-repo AI blurbs. The blurbs themselves fold onto `githubProfile.repos[].summary`/`.themes` (one-to-one with the repos shown), so this cell carries only `status`: `ok` means the blurbs are on the repos, `pending` (with `retryAfterMs`) means re-fetch. There is no `value` here.

            • status string required
              okpending
            • value object
            • retryAfterMs number

              On `pending` only: milliseconds to wait before re-fetching this call. The value is compute-once/cached, so the retry returns `ok` fast once it lands.

          • match_rationale object

            Whether and why this developer matches the search query, judged against it. SEARCH ONLY (there is no query to judge against on an id lookup). `value` is `{ matched, reasons? }`: a real match is `{ matched: true, reasons: ["Currently Staff Engineer at Stripe on payments", "Maintains a Go ISO-20022 library", "8 years shipping Go"] }` — a list of short, positive, recruiter-facing bullets (repos, languages, LinkedIn role/company, organisation, location, tenure). A weak/non-match is `{ matched: false }` (no reasons — an honest verdict, not a manufactured one). Check `matched` before reading `reasons`.

            • status string required
              okpending
            • value object
            • retryAfterMs number

              On `pending` only: milliseconds to wait before re-fetching this call. The value is compute-once/cached, so the retry returns `ok` fast once it lands.

  • 400 Neither `ids` nor `logins` named anyone
    • code string required

      Stable machine-readable error code. Branch on this, never on the numeric status.

      bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_errorbuild_failed
    • message string required

      Human-readable explanation of the refusal.

    • status integer required

      The HTTP status code, repeated in the body.

    • remedy object

      A self-serve path forward, when one exists (a 402 points at how to restore access).

      • kind string required

        What kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.

        topupconnect_mailbox
      • url string required

        Where to go to clear the condition: an API path, or a web app page when only a person can.

  • 401 Missing or invalid API key
    • code string required

      Stable machine-readable error code. Branch on this, never on the numeric status.

      bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_errorbuild_failed
    • message string required

      Human-readable explanation of the refusal.

    • status integer required

      The HTTP status code, repeated in the body.

    • remedy object

      A self-serve path forward, when one exists (a 402 points at how to restore access).

      • kind string required

        What kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.

        topupconnect_mailbox
      • url string required

        Where to go to clear the condition: an API path, or a web app page when only a person can.

  • 402 Your plan does not include this capability
    • code string required

      Stable machine-readable error code. Branch on this, never on the numeric status.

      bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_errorbuild_failed
    • message string required

      Human-readable explanation of the refusal.

    • status integer required

      The HTTP status code, repeated in the body.

    • remedy object

      A self-serve path forward, when one exists (a 402 points at how to restore access).

      • kind string required

        What kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.

        topupconnect_mailbox
      • url string required

        Where to go to clear the condition: an API path, or a web app page when only a person can.

  • 403 The key lacks search:read
    • code string required

      Stable machine-readable error code. Branch on this, never on the numeric status.

      bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_errorbuild_failed
    • message string required

      Human-readable explanation of the refusal.

    • status integer required

      The HTTP status code, repeated in the body.

    • remedy object

      A self-serve path forward, when one exists (a 402 points at how to restore access).

      • kind string required

        What kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.

        topupconnect_mailbox
      • url string required

        Where to go to clear the condition: an API path, or a web app page when only a person can.

  • 429 More than 30 requests a minute
    • code string required

      Stable machine-readable error code. Branch on this, never on the numeric status.

      bad_requestunauthorizedsignature_requiredpayment_requiredforbiddennot_foundconflictgonepayload_too_largeunprocessable_entitytoo_many_requestsinternal_errornot_implementedbilling_unavailablenot_contactablemailbox_link_unavailablemailbox_requiredmail_engine_unavailablewebhook_publisher_unavailabledatabase_unavailableclient_errorbuild_failed
    • message string required

      Human-readable explanation of the refusal.

    • status integer required

      The HTTP status code, repeated in the body.

    • remedy object

      A self-serve path forward, when one exists (a 402 points at how to restore access).

      • kind string required

        What kind of remedy this is, so a client can route it: whether the caller can clear the condition through the API, or a person must act in the web app.

        topupconnect_mailbox
      • url string required

        Where to go to clear the condition: an API path, or a web app page when only a person can.