Diil Docs
  1. Docs
  2. API reference

GET /v1/langs — site languages

Updated:

The smallest endpoint in the API and the one your language switcher has been waiting for. It answers a single question — “which languages does this site speak right now?” — and answers it in the exact order your editors arranged them in the CRM.

Reach for it when you need to:

  • render a language switcher in the header without hard-coding en and de in five places;
  • check that the language from the URL (/de/pricing) actually exists before you pass it to ?lang;
  • generate localized routes, hreflang tags or a sitemap per language.

List active languages

GET/v1/langs

https://back.sitecog.com/content/v1/langs

Returns the active languages of the site as an array, sorted the way they are sorted in the CRM. Languages that are switched off are not included — if a code is not in this list, the API won't accept it in ?lang either.

Parameters

Nothing to tune here: no path parameters, no query parameters. Just the key.

x-crm-keyheaderstringrequired
Your site key. A key is bound to one site, so the answer is always the languages of that site. Can also be passed as ?key= if headers are not an option. See Site keys.

Example request

curl https://back.sitecog.com/content/v1/langs \
  -H "x-crm-key: pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40"

Example response

200 OKjson
[
  { "key": "en", "title": "English", "index": 0 },
  { "key": "de", "title": "German", "index": 1 }
]

Response fields

An array of objects, one per active language. Three fields each — we did promise it was small.

keystring
Language code: two lowercase letters (en, de) or a regional variant like pt-BR. This is exactly the value you pass to ?lang. Case-sensitive: pt-BR and pt-br are different codes.
titlestring
The language name as the editor typed it in the CRM. Want the switcher to say “Deutsch” instead of “German”? Rename it in the CRM — no deploy needed.
indexnumber
Position in the CRM, starting at 0. The array already comes sorted by it, so you rarely need to sort yourself.

Build a language switcher in React

The usual setup: the language lives in the URL (/en/…, /de/…), the switcher is built from /v1/langs, and every content request carries the same code in ?lang. Here is a switcher that knows nothing about specific languages — add Italian in the CRM tomorrow and it simply shows up.

components/LangSwitcher.tsxtsx
type Lang = { key: string; title: string; index: number };

export function LangSwitcher({ langs, current, path }: {
  langs: Lang[];      // straight from GET /v1/langs
  current: string;    // the language of the page being shown
  path: string;       // the rest of the URL, e.g. "/pricing"
}) {
  return (
    <nav aria-label="Language">
      {langs.map((lang) => (
        <a
          key={lang.key}
          href={`/${lang.key}${path}`}
          hrefLang={lang.key}
          aria-current={lang.key === current ? 'true' : undefined}
        >
          {lang.title}
        </a>
      ))}
    </nav>
  );
}

Pairing it with ?lang

The key you get here is the key you send everywhere else. Check the code from the URL against the list first: an unknown code in ?lang is a 400 unknown_lang, and nobody wants an error page because someone typed /fr/ by hand.

app/[lang]/page.tsxtsx
import { notFound } from 'next/navigation';
import { getLangs } from '@/lib/langs';
import { LangSwitcher } from '@/components/LangSwitcher';

export default async function Home({ params }: { params: Promise<{ lang: string }> }) {
  const { lang } = await params;
  const langs = await getLangs();

  // Unknown language in the URL → a normal 404 instead of a 400 from the API
  if (!langs.some((l) => l.key === lang)) notFound();

  const res = await fetch(`https://back.sitecog.com/content/v1/pages/home?lang=${lang}`, {
    headers: { 'x-crm-key': process.env.CRM_KEY! },
    next: { revalidate: 60 },
  });
  const page = await res.json();

  return (
    <>
      <LangSwitcher langs={langs} current={lang} path="" />
      <h1>{page.content.hero.content.hero_title.content[lang]}</h1>
    </>
  );
}

Need a default for visitors who land on /? That is your call — many sites simply take the first language in the list. The API itself never picks a language for you and never substitutes a missing translation; how to handle gaps gracefully is covered in Languages & fallbacks.

Errors

StatusBodyWhat happened
401{"message":"invalid_key"}Missing, malformed or revoked key.
405{"message":"method_not_allowed"}Only GET (and HEAD) are accepted. The API is read-only.
429{"message":"rate_limit_exceeded"}Too many requests this minute. See Rate limits.

The full list with fixes lives on the Errors page.

Tips from the trenches