Developers

    How developers and AI agents can use Sunny Patneedi’s site

    Everything on this page is public and read-only. Use these resources to find, read and cite the site’s pages, and to call its API.

    Last updated

    Machine-readable resources

    • llms.txt GET /llms.txt

      Guidance for AI assistants: when to use this site, which pages are authoritative, and how to describe Sunny’s work accurately. Read it before summarizing or citing the site.

    • Sitemap GET /sitemap.xml

      Every public page, each with its last-modified date.

    • Markdown pages Accept: text/markdown, or ?format=markdown

      Every public page is also available as Markdown with the same text, headings and links. Responses vary by Accept, and each page’s Link header points to its Markdown version.

    • Structured data <script type="application/ld+json">

      Each page’s HTML contains one schema.org JSON-LD graph that describes the site, Sunny Patneedi and his organization. Pages such as About and Contact add their own page type.

    • OpenAPI description GET /openapi.json

      An OpenAPI 3.1 description of the public pages, these discovery files and the API, readable from any origin. Its list of pages always matches the sitemap.

    • API catalog GET /.well-known/api-catalog

      An RFC 9727 API catalog (application/linkset+json) that links to the OpenAPI description, this page, the API health check and the API’s projects and profile endpoints.

    • robots.txt GET /robots.txt

      Crawling rules. Public pages are open to every crawler; admin, sign-in, billing, payment and confirmation routes are excluded.

    Authentication

    None. Every page, file and endpoint on this page is public and read-only. There are no API keys, accounts or tokens.

    API

    Base URL: https://sunnypatneedi.com/api

    The API returns JSON, and every response allows cross-origin reads (Access-Control-Allow-Origin: *).

    • GET /api/projects

      Every project on the projects page, current work first, with its purpose, audience, status and what that status means, last-checked date and verified link. Statuses and dates are exactly as the page shows them; a project without a check date is unverified.

    • GET /api/projects/{projectId}

      One project, by an id from /api/projects such as puddle. An unknown id returns a not_found error.

    • GET /api/profile

      Sunny Patneedi’s public profile: name, title, the approved bios, career, current advisory role, the role he is looking for, verified profile links and how to get in touch. It contains no email addresses.

    • GET /api/healthz

      Reports whether the API server is running. Returns {"status":"ok"}.

    Every API response has a Link header that points to the OpenAPI description (rel="service-desc"), this page (rel="service-doc") and the API catalog (rel="api-catalog").

    The projects and profile JSON is built from the same content as the pages and checked against them on every build. Everything else, including writing and frameworks, is available as HTML or Markdown; the OpenAPI description lists those pages as the getHomePage, getPage and getFrameworkGuide operations.

    Rate limits

    The API allows 120 requests per 60-second window for each client IP address. Every API response reports the limit and what is left of it, using the header fields from draft-ietf-httpapi-ratelimit-headers-11, an IETF draft that is not yet an RFC:

    • RateLimit-Policy: "default";q=120;w=60

      The policy: a quota (q) of 120 requests per window (w) of 60 seconds.

    • RateLimit: "default";r=119;t=60

      Requests remaining in the current window (r) and seconds until it resets (t).

    Over the limit, the API returns 429 Too Many Requests with a Retry-After header, in seconds, and an error with the code rate_limited. Wait that long before retrying.

    Each server instance keeps its own count, so rely on the headers rather than counting requests yourself.

    Pages and discovery files have no rate limit of their own. The hosting platform may still challenge or block automated traffic.

    Errors

    API errors are RFC 9457 problem details, sent with Content-Type: application/problem+json. The site uses the same format for its 400, 404 and 405 errors when a request prefers JSON, for example with Accept: application/json. Browsers and other clients get HTML, Markdown or plain-text errors.

    Fields

    • type: A URI for the kind of problem. Most link to their entry below.
    • title: A short summary of the kind of problem.
    • status: The HTTP status code.
    • detail: What went wrong with this request.
    • instance: The request path. Query strings are never included.
    • code: A stable, machine-readable code. Branch on this field.
    • hint: What to do next, with a link to the relevant documentation.
    • violated-policies: Only on rate_limited errors: the rate-limit policies that were exceeded.

    Error codes

    • bad_request 4xx · API

      The API could not process the request. Check it against the OpenAPI description.

    • malformed_body 400 · API

      The JSON request body could not be parsed.

    • invalid_path 400 · Site

      The path has invalid percent-encoding, an encoded “..” segment or a NUL byte.

    • payload_too_large 413 · API

      The request body is over 100 KB, or a form body has too many fields.

    • unsupported_media_type 415 · API

      The request body uses an unsupported Content-Encoding or charset. Send UTF-8, uncompressed or with gzip, deflate or br.

    • not_found 404 · API and site

      No page, file or API endpoint exists at this path.

    • method_not_allowed 405 · API and site

      The method is not supported at this path. The Allow header lists the methods that are.

    • rate_limited 429 · API

      The client went over the API rate limit. Wait the number of seconds in Retry-After. Its type is the IANA quota-exceeded problem type, https://iana.org/assignments/http-problem-types#quota-exceeded.

    • internal_error 500 · API

      The API failed unexpectedly. Retry later.

    Example: a page that does not exist, requested as JSON

    curl -H "Accept: application/json" https://sunnypatneedi.com/no-such-page
    HTTP/1.1 404 Not Found
    Content-Type: application/problem+json; charset=utf-8
    
    {
      "type": "https://sunnypatneedi.com/developers#error-not-found",
      "title": "Not found",
      "status": 404,
      "detail": "No public page or file exists at this path.",
      "instance": "/no-such-page",
      "code": "not_found",
      "hint": "Find public pages in https://sunnypatneedi.com/sitemap.xml, or read https://sunnypatneedi.com/llms.txt for guidance."
    }

    Example requests

    • Read a page as Markdown

      curl -H "Accept: text/markdown" https://sunnypatneedi.com/about
    • List projects with their status and last-checked date

      curl https://sunnypatneedi.com/api/projects
    • Get one project

      curl https://sunnypatneedi.com/api/projects/puddle
    • Get the public profile

      curl https://sunnypatneedi.com/api/profile
    • Get the OpenAPI description

      curl https://sunnypatneedi.com/openapi.json
    • Get the API catalog

      curl https://sunnypatneedi.com/.well-known/api-catalog
    • Check the API and see its rate-limit headers

      curl -i https://sunnypatneedi.com/api/healthz

    Response from the health check (some headers left out)

    HTTP/1.1 200 OK
    Content-Type: application/json; charset=utf-8
    RateLimit-Policy: "default";q=120;w=60
    RateLimit: "default";r=119;t=60
    
    {"status":"ok"}

    Questions

    For anything not covered here, use the contact page.

    Contact Sunny