Skip to main content
Make sure locales are configured first. See Setting Up Locales.

Fetching localized content via API

After enabling Localization, you will need to update all API calls to include the locale parameter. The locale parameter tells the ButterCMS API which localized version of your content to return.

API endpoint patterns

Single Page

https://api.buttercms.com/v2/pages/<page_type_slug>/<page_slug>/?locale=en&auth_token=your_api_token

Multiple Pages

https://api.buttercms.com/v2/pages/*/?locale=en&page=1&page_size=10&auth_token=your_api_token

Page Types

https://api.buttercms.com/v2/pages/<page_type>/?locale=en&page=1&page_size=10&auth_token=your_api_token

Collections

https://api.buttercms.com/v2/content/<collection>/?locale=en&page=1&page_size=10&auth_token=your_api_token

JavaScript / Node.js examples

Basic setup

import ButterCMS from "buttercms";

const butterCMSClient = ButterCMS("your_api_token");

Fetching a single Page

// Fetch page in a specific locale
const { data } = await butterCMSClient.page.retrieve(
  "landing_page",      // page type
  "home",              // page slug
  {
    locale: "es",      // locale parameter
    preview: 1         // optional: include draft content
  }
);

const { fields } = data.data;
console.log(fields.headline); // Spanish headline

Fetching multiple Pages

// Fetch all pages of a type in Spanish
const response = await butterCMSClient.page.list("blog_posts", {
  locale: "es",
  page: 1,
  page_size: 10,
  order: "-published"
});

const pages = response.data.data;
pages.forEach(page => {
  console.log(page.fields.title);
});

Fetching Collections

// Fetch collection items in French
const { data } = await butterCMSClient.content.retrieve(
  ["team_members", "testimonials"],
  {
    locale: "fr",
    page: 1,
    page_size: 20
  }
);

const teamMembers = data.data.team_members;
const testimonials = data.data.testimonials;

Dynamic locale selection

// Function to fetch content based on user's locale
async function getLocalizedPage(pageType, slug, userLocale) {
  const supportedLocales = ['en', 'es', 'fr', 'de'];
  const locale = supportedLocales.includes(userLocale)
    ? userLocale
    : 'en'; // fallback

  try {
    const response = await butterCMSClient.page.retrieve(pageType, slug, {
      locale
    });
    return response.data.data;
  } catch (error) {
    // Fallback to default locale if content not found
    if (error.response?.status === 404) {
      const fallback = await butterCMSClient.page.retrieve(pageType, slug, {
        locale: 'en'
      });
      return { ...fallback.data.data, _fallback: true };
    }
    throw error;
  }
}

Python examples

Basic setup

from butter_cms import ButterCMS

client = ButterCMS('your_api_token')

Fetching a single Page

# Fetch page in Spanish
response = client.pages.get(
    'landing_page',
    'home',
    params={'locale': 'es'}
)

page = response['data']
print(page['fields']['headline'])

Fetching multiple Pages

# Fetch all blog posts in German
response = client.pages.all(
    'blog_posts',
    params={
        'locale': 'de',
        'page': 1,
        'page_size': 10
    }
)

for page in response['data']:
    print(page['fields']['title'])

Fetching Collections

# Fetch collections in French
response = client.content_fields.get(
    ['team_members', 'faqs'],
    params={'locale': 'fr'}
)

team_members = response['data']['team_members']
faqs = response['data']['faqs']

PHP / Laravel examples

Controller setup

<?php

namespace App\Http\Controllers;

use ButterCMS\ButterCMS;

class LandingPageController extends Controller
{
    private $client;

    public function __construct() {
        $this->client = new ButterCMS('your_api_token');
    }

    public function showPage(string $type, string $pageSlug, string $locale = 'en')
    {
        // Fetch page with locale parameter
        $params = [
            'locale' => $locale,
            'preview' => 1
        ];

        $page = $this->client->fetchPage($type, $pageSlug, $params);

        return view('page', [
            'fields' => $page->getFields(),
            'locale' => $locale
        ]);
    }
}

Fetching Collections

// Fetch navigation items in Spanish
$menuParams = [
    'page' => '1',
    'page_size' => '10',
    'locale' => 'es-es',
    'test' => 1
];

$menuCollection = $this->client->fetchContentFields(
    ['navigation_items'],
    $menuParams
);

$menuItems = $menuCollection['navigation_items'];

Ruby / Rails examples

Basic setup

require 'buttercms-ruby'

ButterCMS::api_token = 'your_api_token'

Fetching a single Page

# Fetch page in Japanese
page = ButterCMS::Page.find('landing_page', 'home', {
  locale: 'ja'
})

puts page['data']['fields']['headline']

Fetching Collections

# Fetch collection in Portuguese
content = ButterCMS::Content.fetch(['team_members'], {
  locale: 'pt-br',
  page: 1,
  page_size: 10
})

team_members = content['data']['team_members']

cURL examples

Fetch single Page

curl -X GET "https://api.buttercms.com/v2/pages/landing_page/home/?locale=es&auth_token=YOUR_API_TOKEN"

Fetch multiple Pages

curl -X GET "https://api.buttercms.com/v2/pages/blog_posts/?locale=de&page=1&page_size=10&auth_token=YOUR_API_TOKEN"

Fetch Collections

curl -X GET "https://api.buttercms.com/v2/content/?keys=team_members,faqs&locale=fr&test=1&auth_token=YOUR_API_TOKEN"

Next.js App Router example

Dynamic locale routing

// app/[locale]/page/[slug]/page.tsx
import ButterCMS from 'buttercms';

const butter = ButterCMS(process.env.BUTTER_CMS_API_KEY);

interface PageProps {
  params: {
    locale: string;
    slug: string;
  };
}

export default async function Page({ params }: PageProps) {
  const { locale, slug } = params;

  const response = await butter.page.retrieve('*', slug, {
    locale,
    preview: process.env.NODE_ENV === 'development' ? 1 : 0,
  });

  const { fields } = response.data.data;

  return (
    <main>
      <h1>{fields.headline}</h1>
      <div dangerouslySetInnerHTML={{ __html: fields.body }} />
    </main>
  );
}

// Generate static paths for all locales
export async function generateStaticParams() {
  const locales = ['en', 'es', 'fr', 'de'];

  // Fetch all page slugs
  const response = await butter.page.list('*', { page_size: 100 });
  const pages = response.data.data;

  // Generate params for each locale + page combination
  return locales.flatMap(locale =>
    pages.map(page => ({
      locale,
      slug: page.slug,
    }))
  );
}

Handling locale in frontend frameworks

Next.js internationalized routing

Configure next.config.js for locale support:
// next.config.js
module.exports = {
  i18n: {
    locales: ["en", "fr", "es", "de"],
    defaultLocale: "en",
  },
};
Then use the locale in your data fetching:
export const getStaticProps = async ({ locale }) => {
  const { data } = await butterCMSClient.page.retrieve(
    "landing_page",
    "home",
    { locale }
  );

  return {
    props: {
      fields: data.data.fields,
    },
  };
};

Previewing localized content

Use the preview parameter to fetch unpublished (draft) content in a specific locale.

Preview mode

To preview unpublished localized content, include the preview parameter:
https://api.buttercms.com/v2/pages/<page_type>/<page_slug>/?locale=es&preview=1&auth_token=your_api_token

Setting up preview URLs

Configure locale-specific preview URLs in your frontend:
// Preview URL generator
function getPreviewUrl(pageSlug, locale) {
  const baseUrl = process.env.PREVIEW_BASE_URL;
  return `${baseUrl}/${locale}/${pageSlug}?preview=true`;
}

// Example: https://preview.example.com/es/homepage?preview=true

Handling missing locale content

When content doesn’t exist in a requested locale, you have several options:

Option 1: fallback to default locale

async function getPageWithFallback(pageType, slug, preferredLocale, defaultLocale = 'en') {
  const butter = Butter('your-api-token');

  try {
    // Try preferred locale first
    return await butter.page.retrieve(pageType, slug, { locale: preferredLocale });
  } catch (error) {
    if (error.response?.status === 404) {
      // Fallback to default locale
      const fallbackPage = await butter.page.retrieve(pageType, slug, { locale: defaultLocale });
      return { ...fallbackPage, _fallbackUsed: true };
    }
    throw error;
  }
}

Option 2: show locale not available message

function LocalizedContent({ locale }) {
  const [content, setContent] = useState(null);
  const [notAvailable, setNotAvailable] = useState(false);

  useEffect(() => {
    fetchContent(locale)
      .then(setContent)
      .catch(() => setNotAvailable(true));
  }, [locale]);

  if (notAvailable) {
    return <div>This content is not yet available in {locale}.</div>;
  }

  return content ? <PageContent data={content} /> : <Loading />;
}

Option 3: redirect to available locale

// Next.js middleware example
export function middleware(request) {
  const locale = request.nextUrl.locale;
  const availableLocales = ['en', 'es', 'fr'];

  if (!availableLocales.includes(locale)) {
    return NextResponse.redirect(new URL('/en' + request.nextUrl.pathname, request.url));
  }
}
For unpublishing and deleting localized content, see Publishing Localized Content.

Best practices

Error handling

Always handle cases where content might not exist in a requested locale:
async function fetchWithFallback(pageType, slug, locale, defaultLocale = 'en') {
  try {
    return await butter.page.retrieve(pageType, slug, { locale });
  } catch (error) {
    if (error.response?.status === 404 && locale !== defaultLocale) {
      console.warn(`Content not found for locale ${locale}, falling back to ${defaultLocale}`);
      return await butter.page.retrieve(pageType, slug, { locale: defaultLocale });
    }
    throw error;
  }
}

Caching considerations

  • Cache responses per locale
  • Invalidate cache when content is updated
  • Consider using ISR (Incremental Static Regeneration) in Next.js
export const revalidate = 60; // Revalidate every 60 seconds

Performance tips

  • Use the fields parameter to request only needed fields
  • Implement pagination for large collections
  • Pre-fetch content for common locale switches