Somalynk Developer Resources

Developer documentation

A read-only content API

Somalynk publishes its content so that people and software can both read it. This page describes what is available, and is deliberately explicit about what is not.

Before you start

  • No API key, no registration, no OAuth. Every endpoint here is public and anonymous.
  • Read-only. GET requests only. There is nothing to create, update or delete.
  • No sandbox. There is one environment, and it is the live site. Since nothing can be written, there is nothing to isolate.
  • No product API. Appointment booking, patient records and the clinical software are not exposed here, on any host, in any form. Do not infer endpoints for them from this page.
  • Rate limits are published. 240 requests per 60 seconds per client address on the JSON endpoints, reported live in the response headers so you never have to guess. See Rate limits below. Cloudflare sits in front of the site and may also challenge unusual traffic.

What you can read

The API is the standard WordPress REST API. The subset below is what Somalynk supports and keeps working. Anything else you find under /wp-json/ belongs to a plugin, may require authentication, and may disappear without notice.

EndpointReturns
GET /wp-json/API index: site metadata and the registered routes.
GET /wp-json/wp/v2/postsPublished blog posts, newest first.
GET /wp-json/wp/v2/posts/{id}One post.
GET /wp-json/wp/v2/pagesPublished pages, including the translated twins.
GET /wp-json/wp/v2/pages/{id}One page.
GET /wp-json/wp/v2/searchCompact search hits across posts and pages.

Useful query parameters: page, per_page (1 to 100), search, slug, offset, order, orderby, and _fields to trim the response. Collection responses carry X-WP-Total, X-WP-TotalPages and a Link header for pagination.

Quickstart

1. List two posts, keeping only the fields you need.

curl "https://somalynk.com/wp-json/wp/v2/posts?per_page=2&_fields=id,slug,link"
[{"id":993,"slug":"supine-lumbar-traction-clinic-adjunct-only",
  "link":"https://somalynk.com/supine-lumbar-traction-clinic-adjunct-only/"},
 {"id":990,"slug":"positional-nerve-root-unloading-home-programme",
  "link":"https://somalynk.com/positional-nerve-root-unloading-home-programme/"}]

2. Search across published content.

curl "https://somalynk.com/wp-json/wp/v2/search?search=knee&per_page=2&_fields=id,title,url,subtype"
[{"id":957,"title":"Prone Knee Bend (Face-Down Control)",
  "url":"https://somalynk.com/prone-knee-bend-face-down-control/","subtype":"post"},
 {"id":963,"title":"Sitting Knee Extension (Seated Flexion Control)",
  "url":"https://somalynk.com/sitting-knee-extension-seated-flexion-control/","subtype":"post"}]

3. What an error looks like. Every failure is JSON in the same shape, with the HTTP status repeated inside the body. Branch on code, never on message.

curl "https://somalynk.com/wp-json/wp/v2/posts?per_page=999"
{"code":"rest_invalid_param",
 "message":"Invalid parameter(s): per_page",
 "data":{"status":400,
         "params":{"per_page":"per_page must be between 1 (inclusive) and 100 (inclusive)"}}}

Other codes you will meet: rest_no_route (404, the route does not exist), rest_post_invalid_id (404, the object does not exist), rest_forbidden_context (401, you asked for context=edit, which needs authentication this API does not offer), and rest_too_many_requests (429, you went over the published rate limit).

Rate limits

The JSON endpoints, meaning /wp-json and /api, allow 240 requests per 60 seconds per client address. HTML pages are not rate limited, and authenticated requests are exempt.

Every JSON response tells you where you stand, so you can pace yourself instead of backing off blindly:

RateLimit-Policy: "somalynk-json";q=240;w=60
RateLimit: "somalynk-json";r=238;t=41
RateLimit-Limit: 240
RateLimit-Remaining: 238
RateLimit-Reset: 41

q is the quota and w the window in seconds; r is what you have left and t the seconds until the window resets. The three RateLimit-Limit style headers are the older spelling of the same numbers, sent for clients that only parse those. If the server cannot read its counter it sends the policy and omits RateLimit: an absent header means unknown, never zero.

Going over the quota returns 429 with a Retry-After header and the same error envelope as everything else:

{"code":"rest_too_many_requests",
 "message":"Too many requests. ...",
 "data":{"status":429,"retry_after":41,
         "policy":{"quota":240,"window":60}}}

The window is fixed, not sliding: your whole quota comes back at once when RateLimit-Reset reaches zero. Cloudflare protections apply separately and are not reflected in these numbers.

Markdown instead of HTML

If you would rather not parse a page of theme markup, ask for markdown. Send an Accept header that prefers text/markdown over text/html and the page is returned as markdown, with Vary: Accept.

curl -H "Accept: text/markdown" https://somalynk.com/faq/

A browser, which asks for text/html explicitly, always receives HTML. The wording of the markdown is identical to the HTML page, and the HTML page stays canonical.

The same documents are also published as plain files under /md/, if a fixed URL suits you better than content negotiation. They are listed in llms.txt.

Machine-readable index

  • openapi.json: OpenAPI 3.1 description of everything above. Every endpoint and parameter in it was probed against this site before publication.
  • llms.txt: a curated plain-text index of the site for AI agents, including the markdown twins.
  • sitemap_index.xml: every published URL, in every language.
  • /.well-known/api-catalog: the RFC 9727 API catalog, served as application/linkset+json. It names this API, its OpenAPI description and this page, for a client that starts with nothing but the domain name.
  • /api: a JSON discovery document naming the same resources. It answers application/json to every client, including a browser, and never redirects to HTML.
  • robots.txt

Languages

The site runs in English, French, Dutch and Spanish. Translations are separate objects with their own id, slug and link, so a page and its translations appear as distinct records in /wp-json/wp/v2/pages. There is no language query parameter: filter on the link prefix, or start from the sitemap.

Using clinical content

Much of what this API returns is clinical writing. It is general information, not individual medical advice. If you surface it to an end user, do not present it as a diagnosis or as a treatment instruction for their situation, and keep the link back to the source page.

Questions, or something here is wrong? Get in touch.

Scroll to Top