headlessblog / sdk
PHP SDK for Headless.Blog API
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.8
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-08 17:41:27 UTC
README
Welcome to the official Headless.Blog PHP Developer Guide. This SDK provides a high-performance, strictly typed, multi-lingual, and completely database-agnostic interface for PHP applications, Laravel frameworks, decoupled monoliths, or any server-side integrations to consume blog content.
By utilizing this SDK, your backend or frontend application acts purely as a headless consumer. All complex business logic, multi-tenant security, taxonomy grouping, vector searches, multi-language translation group linkages, CDN optimizations, and data aggregation are handled securely by the Headless.Blog backend.
Installation
Install the package via Composer. The SDK requires PHP 8.2+ and uses Guzzle for HTTP requests.
composer require headlessblog/sdk
Initialization & Core Architecture
1. Strict Token Authentication & Configuration
The Headless API relies on strict API token authentication to isolate multi-tenant data. Initialize the SDK using the Config object:
use HeadlessBlog\Sdk\Config; use HeadlessBlog\Sdk\Client; // Initialize Configuration with your active API key from the SaaS Dashboard $config = new Config( apiKey: 'your_api_token_here', baseUrl: 'https://api.headless.blog/v1', // Optional, defaults to this URL timeout: 30, // Optional, timeout in seconds defaultLocale: 'en' // Optional, default fallback language ); // Instantiate the SDK Client $client = new Client($config);
Equal-Peer Multi-Language (i18n) Architecture
The Headless.Blog API implements an Equal-Peer Multi-Language Architecture anchored on shared translation group UUIDs (translation_group_id).
1. Locale Resolution Order
When requesting localized content from any endpoint, the target locale is resolved using the following hierarchy:
- Explicit Query Parameter: Passed directly to resource methods (e.g.
$client->posts->get($id, locale: 'es')or$client->bootstrap->get(locale: '*')for wildcard). - HTTP
Accept-LanguageHeader: Automatically managed when you set a default locale onConfigor call$client->setLocale('es'). - Tenant Default Language Fallback: Automatically falls back to the tenant's
defaultLocaleif no locale is matched.
2. Setting Active Locale Dynamically (e.g., in Middleware)
In web applications (Laravel middleware, Symfony request listeners, custom routers), you can set the user's active locale dynamically on the client instance:
// Option A: Set locale globally on the current client instance $client->setLocale('es'); // Retrieve currently active locale $currentLocale = $client->getLocale(); // 'es' // Option B: Immutably clone the client with a new locale // (Recommended for long-lived application runtimes like Laravel Octane, Swoole, RoadRunner) $spanishClient = $client->withLocale('es');
3. Response Meta Envelope & Alternate Translations
Response objects from collection/aggregate endpoints include a meta envelope detailing locale resolution. Individual content items contain an availableTranslations array referencing alternate language versions:
$posts = $client->posts->list(['limit' => 10]); // Access Meta Envelope $meta = $posts['meta']; // [ // 'defaultLocale' => 'en', // 'supportedLocales' => ['en', 'es', 'hu'], // 'resolvedLocale' => 'es', // 'requestedLocale' => 'es', // 'source' => 'query' // ] // Access Entity Alternate Translations $post = $posts['posts'][0]; $translations = $post['availableTranslations']; // [ // [ // 'id' => 'post-uuid-en', // 'locale' => 'en', // 'title' => 'Understanding Headless Architecture', // 'slug' => 'understanding-headless-architecture', // 'url_link' => 'https://myblog.com/blog/understanding-headless-architecture' // ] // ]
4. Building Language Switchers & hreflang Head Tags
Using availableTranslations and meta['supportedLocales'], you can easily render UI language selectors and SEO header tags:
// Render HTML hreflang tags for SEO foreach ($post['availableTranslations'] as $translation) { echo sprintf( '<link rel="alternate" hreflang="%s" href="%s" />' . "\n", htmlspecialchars($translation['locale']), htmlspecialchars($translation['url_link']) ); }
Global Error Handling & Exceptions
All API errors throw specific exceptions extending HeadlessBlog\Sdk\Exception\HeadlessBlogException.
BadRequestException (HTTP 400)
Thrown when query parameters or body payloads fail backend validation.
try { $client->posts->list(['limit' => 500]); // Exceeds max limit } catch (\HeadlessBlog\Sdk\Exception\BadRequestException $e) { echo $e->getMessage(); print_r($e->getDetails()); // Specific field validation failures }
UnauthorizedException (HTTP 401 / 403)
Thrown when the API token is missing, invalid, expired, or restricted by plan limits.
RateLimitException (HTTP 429)
Thrown when client IP exceeds sliding-window rate limits.
ServerException (HTTP 500+)
Thrown on catastrophic server or database errors.
Endpoint Reference Guide
All resource methods return parsed associative arrays.
1. Application Bootstrap & Navigation
Delivers foundational categories, tags, multi-dimensional taxonomies, and post types filtered by locale.
Method:
// @param string|null $locale Optional ISO language code or '*' for all $bootstrap = $client->bootstrap->get(locale: 'es');
Response Schema:
[
'meta' => [
'defaultLocale' => 'en',
'supportedLocales' => ['en', 'es', 'hu'],
'resolvedLocale' => 'es',
'requestedLocale' => 'es',
'source' => 'query'
],
'categories' => [
[
'id' => 12,
'translationGroupId' => 'tg-cat-01',
'locale' => 'es',
'parentId' => null,
'name' => 'Recetas',
'slug' => 'recetas',
'url_link' => 'https://myblog.com/blog/category/recetas',
'description' => 'Recetas deliciosas',
'imagePath' => 'https://cdn.headless.blog/...',
'position' => 0,
'availableTranslations' => [
['locale' => 'en', 'slug' => 'recipes', 'name' => 'Recipes']
]
]
],
'tags' => [ /* Localized Tags */ ],
'taxonomies' => [ /* Localized Taxonomies */ ],
'postTypes' => [ /* Localized Post Types */ ]
]
2. Website Settings
Delivers general website settings and multi-language configuration.
Method:
$settings = $client->settings->get();
Response Schema:
[
'id' => 'uuid',
'websiteName' => 'My Blog',
'websiteBaseUrl' => 'https://myblog.com',
'blogLandingPageUrl' => 'blog',
'blogLandingPageTitle' => 'Read Our Blog',
'metaSeoKeywords' => 'blog, kitchen, travel',
'highlightedCategoryId' => 5,
'semanticSearchHeadlessApi' => true,
'semanticSearchWebsite' => true,
'defaultLocale' => 'en',
'supportedLocales' => ['en', 'es', 'hu']
]
3. Consolidated Homepage Data
Aggregates localized hero, recent, featured, and favorite post sections in a single response to eliminate UI waterfall loading.
Method:
// @param string|null $locale Optional ISO language code $homeData = $client->home->get(locale: 'es');
Response Schema:
[
'meta' => [ /* Resolution Meta */ ],
'section-hero' => [ /* Post object */ ],
'section-recent' => [ /* Array of 5 MobilePostSnippets */ ],
'section-featured' => [ /* Array of 6 MobilePostSnippets */ ],
'section-favorite' => [ /* Array of Curated Highlights */ ]
]
4. Testimonials API
Serves a paginated list of enabled customer testimonials filtered by locale.
Method:
// @param int $page Page number (default: 1) // @param int $limit Items per page (default: 10, max: 50) // @param string|null $locale Optional ISO language code $testimonials = $client->testimonials->list(page: 1, limit: 10, locale: 'es');
Response Schema:
[
'meta' => [ /* Resolution Meta */ ],
'pagination' => ['totalItems' => 12, 'totalPages' => 2, 'currentPage' => 1, 'limit' => 10],
'testimonials' => [
[
'id' => 'testim-uuid-es',
'translationGroupId' => 'tg-testim-01',
'locale' => 'es',
'authorName' => 'Carlos Mendoza',
'authorTitle' => 'Chef Ejecutivo',
'authorCompany' => 'Restaurante Madrid',
'avatarUrl' => 'https://cdn.headless.blog/...',
'quote' => 'Excelente plataforma headless.',
'rating' => 5,
'createdAt' => '2026-08-01T10:00:00Z',
'availableTranslations' => [
['id' => 'testim-uuid-en', 'locale' => 'en', 'authorName' => 'Carlos Mendoza']
]
]
]
]
5. Hybrid Search API
Executes high-speed semantic vector search or classic SQL keyword search strictly filtered by locale.
Method:
// @param string $query Required search string // @param string|null $locale Optional ISO language code $results = $client->search->query(query: 'Cómo Hornear Pan', locale: 'es');
Response Schema:
[
[
'id' => 'post-uuid-es',
'title' => 'Cómo Hornear Pan',
'slug' => 'como-hornear-pan',
'url_link' => 'https://myblog.com/blog/como-hornear-pan',
'excerpt' => 'Guía para principiantes.',
'featuredImagePathThumbnail' => 'https://cdn.headless.blog/...',
'contentType' => 'markdown',
'categories' => [['id' => 12, 'name' => 'Recetas', 'slug' => 'recetas', 'url_link' => 'https://...']],
'tags' => [['name' => 'Panadería', 'slug' => 'panaderia', 'url_link' => 'https://...']]
]
]
6. Posts Index & Filtering
Serves paginated lists of blog posts with availableTranslations metadata.
Method:
// @param array $filters Key-value parameters (page, limit, category, tag, taxonomyGroup, taxonomyTerm, postType, locale) $postsData = $client->posts->list([ 'page' => 1, 'limit' => 12, 'category' => 'recetas', 'locale' => 'es' ]);
Response Schema:
[
'meta' => [ /* Resolution Meta */ ],
'pagination' => ['totalItems' => 42, 'totalPages' => 4, 'currentPage' => 1, 'limit' => 12],
'posts' => [
[
'id' => 'post-uuid-es',
'translationGroupId' => 'tg-post-100',
'locale' => 'es',
'title' => 'Arquitectura Headless Explicada',
'slug' => 'arquitectura-headless-explicada',
'url_link' => 'https://myblog.com/blog/arquitectura-headless-explicada',
'excerpt' => 'Un resumen ejecutivo.',
'featuredImagePath' => 'https://cdn.headless.blog/...',
'publishedAt' => '2026-08-08T12:00:00Z',
'categories' => [ /* MobileCategorySnippets */ ],
'tags' => [ /* MobileTagSnippets */ ],
'availableTranslations' => [
[
'id' => 'post-uuid-en',
'locale' => 'en',
'title' => 'Understanding Headless Architecture',
'slug' => 'understanding-headless-architecture',
'url_link' => 'https://myblog.com/blog/understanding-headless-architecture'
]
]
]
]
]
7. Post Details
Fetches comprehensive article details, SEO metadata, FAQs, and alternate translations array.
Methods:
// @param string $id Lookup by post UUID // @param string|null $locale Optional ISO language code $post = $client->posts->get(id: 'post-uuid-es', locale: 'es'); // @param string $slug Lookup by URL slug // @param string|null $locale Optional ISO language code $post = $client->posts->getBySlug(slug: 'arquitectura-headless-explicada', locale: 'es');
8. Comments Engine
Retrieves or submits comments for a specific post.
Retrieve Comments:
$comments = $client->posts->getComments('post-uuid-es');
Submit a Comment:
$response = $client->posts->addComment('post-uuid-es', [ 'content' => '¡Excelente artículo!', 'authorName' => 'Maria Garcia', 'authorEmail' => 'maria@example.com', 'parentId' => null ]);
9. Taxonomies & Metadata
Categories & Tags:
$categories = $client->categories->list(locale: 'es'); $category = $client->categories->getBySlug('recetas', locale: 'es'); $tags = $client->tags->list(locale: 'es'); $tag = $client->tags->getBySlug('sin-azucar', locale: 'es');
Taxonomies:
$taxonomies = $client->taxonomies->list(locale: 'es'); $group = $client->taxonomies->getByGroupSlug('dificultad', locale: 'es'); $term = $client->taxonomies->getByTermSlug('dificultad', 'facil', locale: 'es');
Post Types & Content Types:
$postTypes = $client->postTypes->list(locale: 'es'); $contentTypes = $client->contentTypes->list();
10. Newsletter Subscription
Registers a subscriber to the tenant's mailing list.
Method:
$response = $client->newsletter->subscribe(email: 'user@example.com', name: 'Maria Garcia');
11. Multi-Lingual Sitemap Generation
Generates standard sitemap items with alternates array grouping multi-lingual versions sharing the same translation_group_id.
Method:
$sitemapData = $client->sitemap->list();
Response Schema:
[
[
'url' => 'https://myblog.com/blog/arquitectura-headless-explicada',
'locale' => 'es',
'translationGroupId' => 'tg-post-100',
'lastModified' => '2026-08-08T12:00:00Z',
'changeFrequency' => 'weekly',
'priority' => 0.7,
'alternates' => [
['locale' => 'en', 'url' => 'https://myblog.com/blog/understanding-headless-architecture']
]
]
]
12. New Posts Since
Returns count of newly published posts since a given ISO timestamp.
Method:
$result = $client->posts->getNewPostsSince('2026-08-01T00:00:00Z');
Contributing & Development
Local Setup & Testing
- Clone the repository:
git clone https://github.com/headlessblog/php-sdk.git cd php-sdk - Install dependencies:
composer install
- Run the unit test suite:
php test/sdk_unit_test.php
- Run integration tests with a live API key:
HEADLESS_BLOG_API_KEY="your_api_key" php test/api_test.php
License
This SDK is open-sourced software licensed under the MIT license.