Localizing pages

Learn how to use the Data API to localize page content and metadata across different locales.

Webflow’s Data API enables you to localize DOM content and metadata across different locales. Localizing pages requires two main steps:

  1. Retrieve data from the primary locale
  2. Update data in the secondary locale

Workflows

There are two main workflows for localizing pages:

DOM Content

The DOM content endpoints enable you to localize text for any secondary locale. The process involves the following steps:

1

Fetch primary locale content

Use the Get page content endpoint to retrieve the page’s DOM structure and default content.

2

Translate content

Traverse the nodes array in the response to identify and translate all text content.

3

Update secondary locale content

Use the localeId parameter in your request to the Update Page Content endpoint to apply the localized content.


Retrieve page content

Start by getting the content structure for a given static page. You’ll need the pageId, which you can find by listing all pages for a site using the list pages endpoint. Once you have the pageId, you can use the get page content endpoint to retrieve the content structure for the page.

Request
1curl -G https://api.webflow.com/v2/pages/<PAGE_ID>/dom \
2 -H "Authorization: Bearer <token>" \
3 -d limit=100 \
4 -d offset=0

Note: This API will only return content for static pages. Dynamic pages, like CMS Collection templates will return an empty response.

Response

The response contains a nodes array with the page’s static content. Each node includes a type property that defines its content type.


pageId
stringRequired

The unique identifier for the page

nodes
list of objectsRequired

The list of nodes that represent the static content of the page

pagination
objectRequired

The pagination information for the response

lastUpdated
stringRequired

The date the page was most recently updated

Response
1{
2 "pageId": "658205daa3e8206a523b5ad4",
3 "nodes": [
4 {
5 "id": "a245c12d-995b-55ee-5ec7-aa36a6cad623",
6 "type": "text",
7 "text": {
8 "html": "<h1>The Hitchhiker's Guide to the Galaxy</h1>",
9 "text": "The Hitchhiker's Guide to the Galaxy"
10 },
11 "attributes": {}
12 },
13 {
14 "id": "a245c12d-995b-55ee-5ec7-aa36a6cad627",
15 "type": "text",
16 "text": {
17 "html": "<div><h3>Don't Panic!</h3><p>Always know where your towel is.</p></div>",
18 "text": null
19 },
20 "attributes": {
21 "number": "forty two"
22 }
23 },
24 {
25 "id": "a245c12d-995b-55ee-5ec7-aa36a6cad629",
26 "type": "image",
27 "image": {
28 "alt": "Marvin, the Paranoid Android",
29 "assetId": "659595234426a9fcbad57043"
30 },
31 "attributes": {}
32 },
33 {
34 "id": "a245c12d-995b-55ee-5ec7-aa36a6cad635",
35 "type": "select",
36 "choices": [
37 {
38 "value": "choice-1",
39 "text": "First choice"
40 },
41 {
42 "value": "choice-2",
43 "text": "Second choice"
44 }
45 ],
46 "attributes": {}
47 },
48 {
49 "id": "a245c12d-995b-55ee-5ec7-aa36a6cad642",
50 "type": "text-input",
51 "placeholder": "Enter something here...",
52 "attributes": {}
53 },
54 {
55 "id": "a245c12d-995b-55ee-5ec7-aa36a6cad671",
56 "type": "submit-button",
57 "value": "Submit",
58 "waitingText": "Submitting...",
59 "attributes": {}
60 },
61 {
62 "id": "a245c12d-995b-55ee-5ec7-aa36a6cad631",
63 "type": "component-instance",
64 "componentId": "6258612d1ee792848f805dcf",
65 "propertyOverrides": [
66 {
67 "propertyId": "a245c12d-995b-55ee-5ec7-aa36a6cad633",
68 "type": "Plain Text",
69 "label": "Catchphrase",
70 "text": {
71 "html": null,
72 "text": "Don't Panic!"
73 }
74 },
75 {
76 "propertyId": "a245c12d-995b-55ee-5ec7-aa36a6cad635",
77 "type": "Rich Text",
78 "label": "Tagline",
79 "text": {
80 "html": "<div><p>Always know where your towel is.</p></div>",
81 "text": null
82 }
83 }
84 ]
85 }
86 ],
87 "pagination": {
88 "limit": 4,
89 "offset": 0,
90 "total": 4
91 },
92 "lastUpdated": "2016-10-24T19:42:38.929Z"
93}

By default, if you don’t include a localeId in your request, the response will return content from the primary locale.

Nodes

The page content endpoint returns a list of nodes that represent static text and images available for localization. Note: this endpoint doesn’t return the entire DOM structure of the page, but only the static content available for localization.

Node types

Different node types represent different kinds of content, each with its own structure and properties.

Node typeDescription
textRepresents text content. Including headings, text blocks, rich text, form labels, and other text content on a page.
imageRepresents static images on a page. It contains alt text details for accessibility and the assetId for fetching the actual image resource.
text-inputRepresents a textinput and textarea fields on a form.
selectRepresents a select field and its options on a form.
submit-buttonRepresents a submit button on a form. It contains the button text and waiting text of the button.
search-buttonRepresents the button text of a search button on a site search element.
component-instanceRepresents a component instance on a page. Learn more about localizing components in the guide.

Node properties

Each node type has a specific structure and properties that define the content it contains. However, all nodes will have the following properties:

id
stringRequired

Node UUID

type
enumRequired

The type of the node.


Acceptable values: text, image, text-input, select, submit-button, search-button, component-instance

attributes
object

The custom attributes of the node. These are typically used to store custom data on the node like aria-labels for accessibility or data-w-id.


Node properties by type

Each node type has a unique structure for accessing page content. For example, a text node contains a text object, which typically includes html and text properties. These properties provide context for strings that can be localized. The tabs below detail the specific properties for each node type.


id
stringRequired

Node UUID

type
enumRequired

The type of the node. text

text
objectRequired

The text content of the node

html
stringRequired

The HTML content of the node

text
stringRequired

The text content of the node

attributes
map from strings to strings

The custom attributes of the node

Text node example
1{
2 "id": "a245c12d-995b-55ee-5ec7-aa36a6cad623",
3 "type": "text",
4 "text": {
5 "html": "<h1>Don't Panic!</h1>",
6 "text": "Don't Panic!"
7 },
8 "attributes": {}
9},
10{
11 "id": "a245c12d-995b-55ee-5ec7-aa36a6cad627",
12 "type": "text",
13 "text": {
14 "html": "<span data-w-id=\"b3107...\">$9.99</span>",
15 "text": "$9.99"
16 },
17 "attributes": {}
18}
Nested HTML tags

The text.html property may contain nested HTML tags with data-w-id attributes (e.g., data-w-id="some-unique-identifier"). Retain these identifiers when updating page content in secondary locales to preserve custom attributes and links on inner HTML elements.

Update page content

Once you’ve identified and translated the content you want to localize, you can update it using the update page content endpoint. You’ll need to pass in the localeId of the secondary locale you want to update as a query parameter, and a list of nodes to update in the request body.

Each node should have the nodeId of the node you want to update, and the node value you want to update. The node value will vary depending on the node type.

POST
/v2/pages/:page_id/dom
1curl -X POST "https://api.webflow.com/v2/pages/63c720f9347c2139b248e552/dom?localeId=localeId" \
2 -H "Authorization: Bearer <token>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "nodes": [
6 {
7 "nodeId": "a245c12d-995b-55ee-5ec7-aa36a6cad623",
8 "text": "<h1>The Hitchhiker\'s Guide to the Galaxy</h1>"
9 },
10 {
11 "nodeId": "a245c12d-995b-55ee-5ec7-aa36a6cad627",
12 "text": "<div><h3>Don\'t Panic!</h3><p>Always know where your towel is.</p></div>"
13 },
14 {
15 "nodeId": "a245c12d-995b-55ee-5ec7-aa36a6cad635",
16 "choices": [
17 {
18 "value": "choice-1",
19 "text": "First choice"
20 },
21 {
22 "value": "choice-2",
23 "text": "Second choice"
24 }
25 ]
26 },
27 {
28 "nodeId": "a245c12d-995b-55ee-5ec7-aa36a6cad642",
29 "placeholder": "Enter something here..."
30 },
31 {
32 "nodeId": "a245c12d-995b-55ee-5ec7-aa36a6cad671",
33 "value": "Submit",
34 "waitingText": "Submitting..."
35 },
36 {
37 "nodeId": "a245c12d-995b-55ee-5ec7-aa36a6cad629",
38 "propertyOverrides": [
39 {
40 "propertyId": "7dd14c08-2e96-8d3d-2b19-b5c03642a0f0",
41 "text": "<div><h1>Time is an <em>illusion</em></h1></div>"
42 },
43 {
44 "propertyId": "7dd14c08-2e96-8d3d-2b19-b5c03642a0f1",
45 "text": "Life, the Universe and Everything"
46 }
47 ]
48 }
49 ]
50}'
Updating text nodes

When updating text nodes, pass the text property with the translated text, structured as the HTML content received from the Get Page Content endpoint. The HTML tag structure must remain identical to the Get Content endpoint’s response.

Updating images isn't supported by the API

Currently, updating images isn’t supported by the API. To update images, you’ll need to update the image asset in the Webflow designer.

Metadata

The metadata endpoints enable you to localize SEO and Open Graph content for any secondary locale. The process involves the following steps:

1

Retrieve primary locale metadata

Use the Get page metadata endpoint to retrieve the page’s metadata.

2

Translate metadata

Translate the metadata for the secondary locale.

3

Update secondary locale metadata

Use the Update page metadata endpoint to update the metadata for the secondary locale.

Retrieve page metadata

First, retrieve the page metadata for the primary locale using the Get page metadata endpoint.

This endpoint will return metadata shown above for the primary locale. The response includes both internal and external information about your webflow page, including unique identifiers, draft information, and publish times.

For a full list of information returned see the Get page metadata page in the API reference.

Request
GET
/v2/pages/:page_id
1curl -G https://api.webflow.com/v2/pages/63c720f9347c2139b248e552 \
2 -H "Authorization: Bearer <token>" \
3 -d localeId=65427cf400e02b306eaa04a0
Response
Response
1{
2 "id": "6596da6045e56dee495bcbba",
3 "siteId": "6258612d1ee792848f805dcf",
4 "title": "Guide to the Galaxy",
5 "slug": "guide-to-the-galaxy",
6 "parentId": {},
7 "collectionId": {},
8 "createdOn": "2024-03-11T10:42:00.000Z",
9 "lastUpdated": "2024-03-11T10:42:42.000Z",
10 "archived": false,
11 "draft": false,
12 "canBranch": false,
13 "isBranch": true,
14 "branchId": "68026fa68ef6dc744c75b833",
15 "seo": {
16 "title": "The Ultimate Hitchhiker's Guide to the Galaxy",
17 "description": "Everything you need to know about the galaxy, from avoiding Vogon poetry to the importance of towels."
18 },
19 "openGraph": {
20 "title": "Explore the Cosmos with The Ultimate Guide",
21 "titleCopied": false,
22 "description": "Dive deep into the mysteries of the universe with your guide to everything galactic.",
23 "descriptionCopied": false
24 },
25 "localeId": "653fd9af6a07fc9cfd7a5e57",
26 "publishedPath": "/en-us/guide-to-the-galaxy"
27}

Update page metadata

To update metadata for a secondary locale, translate the properties listed below and include them in the request body when calling the Update page metadata endpoint.

Request

titlestringOptional
Title for the page
slugstringOptional
Slug for the page. **Note:** Updating slugs in secondary locales is only supported in <a href="https://webflow.com/localization">Advanced and Enterprise localization add-on plans.</a>
seoobjectOptional
SEO-related fields for the Page
openGraphobjectOptional
Open Graph fields for the Page
1curl -X PUT https://api.webflow.com/v2/pages/<PAGE_ID>?localeId=<SECONDARY_LOCALE_ID> \
2 -H "Authorization: Bearer <token>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "title": "Página de inicio - Mi sitio web",
6 "seo": {
7 "title": "Mi sitio web - Página de inicio",
8 "description": "Descripción SEO en español para mejor visibilidad en buscadores"
9 },
10 "openGraph": {
11 "title": "Mi sitio web",
12 "description": "Descripción para compartir en redes sociales"
13 }
14 }'

Slug localization is only available with specific Webflow plans:

  • Localize Advanced add-on
  • Enterprise Localization

Complete localization workflow

Here’s a complete example that demonstrates the full page localization process for static content (not including components):

Node.js
1const client = new WebflowClient({ accessToken: "YOUR_ACCESS_TOKEN" });
2
3// Localize page static content and metadata
4// Assume `translations` object holds page-level translations in memory for the secondary locale
5async function localizePage(pageId, secondaryLocaleId, translations) {
6 // 1. Get primary locale content structure
7 const contentData = await client.pages.getContent(pageId);
8
9 // 2. Prepare static content updates
10 const staticUpdates = {
11 nodes: contentData.nodes
12 .filter(node => node.type === 'text' && translations.static[node.id])
13 .map(node => ({
14 nodeId: node.id,
15 text: translations.static[node.id]
16 }))
17 };
18
19 // 3. Update page content
20 if (staticUpdates.nodes.length > 0) {
21 await client.pages.updateStaticContent(pageId, {
22 localeId: secondaryLocaleId,
23 nodes: staticUpdates.nodes,
24 });
25 }
26
27 // 4. Update page metadata
28 if (translations.metadata) {
29 await client.pages.updatePageSettings(pageId, {
30 localeId: secondaryLocaleId,
31 title: translations.metadata.title,
32 seo: {
33 title: translations.metadata.seo.title,
34 description: translations.metadata.seo.description,
35 },
36 openGraph: {
37 title: translations.metadata.openGraph.title,
38 description: translations.metadata.openGraph.description,
39 },
40 });
41 }
42}

FAQ

No, you only need to include the nodes you want to update in the request body. If you don’t include a node in the Update Page Content request body, that element will inherit the content from the primary locale.

If an error occurs when attempting to update a node on a page, the request may still return a 200 status code, but you may want to check the response body for the errors array to see if any errors surfaced.

Because the primary locale is the source of truth for content, changes must be initiated through the Webflow canvas at this time.

Component instances are included in the Get Page Content API response to provide a more wholistic view of the content on a page.

When getting page content for the primary locale, all component instances are included in the response. When getting page content for a secondary locale, only component instances with property overrides are included in the response.

Learn more in the Components and properties guide.

When making updates to your site via Data APIs, you may need to refresh the page in order to see the changes reflect in the Webflow canvas.