Quick Start

In this lesson you will add djangocms-rest to a django CMS project and walk the full content path the API exposes: list the configured languages, fetch the home page, and then follow the link it gives you to retrieve the actual plugin content of a placeholder.

By the end you will understand the two-step shape of the API — pages carry placeholders, placeholders carry content — which is the key to everything else in these docs.

Note

You need a working django CMS 4.1+ project with at least one published page. If you do not have one yet, follow Installing django CMS by hand first.

Step 1 — Install the package

pip install djangocms-rest

This also installs Django REST framework, which djangocms-rest is built on and requires — you do not install it separately. The OpenAPI schema and browsable docs come from drf-spectacular, which is optional and covered in Generate the OpenAPI schema and a typed client.

Step 2 — Enable the apps

Add rest_framework and djangocms_rest to INSTALLED_APPS:

# settings.py
INSTALLED_APPS = [
    # ... your other apps
    "cms",
    "rest_framework",
    "djangocms_rest",
]

Note

rest_framework enables Django REST framework’s browsable API in the browser. The JSON API itself works without it in INSTALLED_APPS, but adding it is recommended.

Step 3 — Mount the URLs

Include the djangocms-rest URLconf in your project’s urls.py. The mount point is up to you; this tutorial uses api/.

# urls.py
from django.urls import path, include

urlpatterns = [
    # ... your other patterns
    path("api/", include("djangocms_rest.urls")),
]

Step 4 — Start the server

python manage.py runserver 8080

Step 5 — List the languages

The languages endpoint is the simplest one and needs no language prefix. It reflects your django CMS CMS_LANGUAGES configuration.

curl http://localhost:8080/api/languages/

You get back a list of every language configured for the current site. Each entry carries a public flag — non-public languages (here, fr) are listed too, so a language switcher can decide what to show:

[
  {
    "code": "en",
    "name": "English",
    "public": true,
    "fallbacks": ["it"],
    "redirect_on_fallback": true,
    "hide_untranslated": false
  },
  {
    "code": "it",
    "name": "Italiano",
    "public": true,
    "fallbacks": ["en"],
    "redirect_on_fallback": true,
    "hide_untranslated": false
  },
  {
    "code": "fr",
    "name": "French",
    "public": false,
    "fallbacks": ["en", "it"],
    "redirect_on_fallback": true,
    "hide_untranslated": false
  }
]

Pick a public code from the response — this tutorial uses en. Every content endpoint is prefixed with a language code.

Step 6 — Fetch the home page

Request the home page for your language. The path-less pages/ endpoint returns the site’s root page.

curl http://localhost:8080/api/en/pages/

The response is a single page object. Note the placeholders array near the bottom — each entry describes a content region of the page and embeds its serialized content (abridged below; your timestamps and ids will differ):

{
  "title": "Home",
  "page_title": "Home",
  "menu_title": "Home",
  "meta_description": "",
  "redirect": "",
  "in_navigation": true,
  "soft_root": false,
  "template": "INHERIT",
  "xframe_options": "",
  "limit_visibility_in_menu": false,
  "language": "en",
  "path": "",
  "absolute_url": "http://localhost:8080/",
  "is_home": true,
  "login_required": false,
  "languages": ["en"],
  "is_preview": false,
  "application_namespace": "",
  "creation_date": "2026-06-25T20:25:15.694029Z",
  "changed_date": "2026-06-25T20:25:15.694137Z",
  "details": "http://localhost:8080/api/en/pages/",
  "placeholders": [
    {
      "slot": "content",
      "label": "Content",
      "language": "en",
      "content": [
        {
          "id": 1,
          "parent_plugin_type": null,
          "plugin_type": "TextPlugin",
          "body": "<p>Hello World!</p>",
          "json": {
            "type": "doc",
            "content": [
              {
                "type": "paragraph",
                "attrs": {"textAlign": "left"},
                "content": [{"text": "Hello World!", "type": "text"}]
              }
            ]
          },
          "rte": ""
        }
      ],
      "details": "http://localhost:8080/api/en/placeholders/18/1/content/",
      "html": ""
    }
  ]
}

This is the central idea of the API:

  • A page carries metadata (title, URL, template, navigation flags …) plus its placeholders.

  • Each placeholder carries a list of serialized plugins in content.

  • Each plugin is a flat JSON object identified by plugin_type.

Where to go next

Now that the basics work, reach for a guide when you have a concrete goal:

To understand why the API is shaped this way, read The content model. For the full list of endpoints and fields, see the Reference.