# Multiple blogs on one site

One site can carry several blogs. A **site** is a verified domain plus the
`pk_…` key that serves its content; a **blog** is an editorial unit mounted
at a base path under that domain — with its own topic queue, schedule,
theme, and delivery target.

Reasons people run more than one:

- **Two languages on one domain** — Romanian at `/blog`, English at
  `/en/blog`. That's the worked example below.
- **Two audiences** — a buyer-facing product blog and a practitioner-facing
  engineering blog, on different cadences.
- **Two destinations** — one blog embedded on your site, another pushed
  into [WordPress](/docs/wordpress).

## The model

:::tree

- example.com (site: one verified domain, one pk key, one plan)
  - /blog (blog: Romanian, weekly, its own palette)
    - /blog/facturare-electronica-2026
  - /en/blog (blog: English, biweekly, its own palette)
    - /en/blog/choosing-an-invoicing-vendor

:::

Delivery resolves a blog by the path **as it appears on your site**, and the
longest matching base path wins. So blogs can sit side by side (`/blog` and
`/en/blog`) or nest (`/blog` and `/blog/ro`) — a request for
`/blog/ro/some-post` goes to the `/blog/ro` blog, not to the `/blog` one.
Base paths are unique per site; reusing one returns
`409 Base path taken`.

## What's per blog, what's per site

This table is the whole decision. If the two things you want to separate are
all in the left column, use two blogs. If any of them are in the right
column, you need two sites — see the last section but one.

| Per blog | Per site |
|---|---|
| Name and base path | Verified domain and `pk_…` public key |
| Its posts, and which ones appear at that path | Content profile — industry, **audience**, **tone**, brand voice, banned topics |
| Topic queue (briefs are queued, approved and rejected per blog) | Link targets — the money pages every post links to |
| Schedule: cadence, posts per period, timezone, publish hour, pause | Article and image quota, plan, and invoice |
| Auto-publish | `sitemap.xml` and `rss.xml` (both cover every blog on the site) |
| Featured images on/off | Visual identity scanned from your website |
| [MD2 theme](/docs/theming), including the dark variant | API keys |
| [Webhooks](/docs/webhooks) | |
| [WordPress delivery target](/docs/wordpress) | |

:::callout{severity=warning title="The content profile is shared, and it's the thing that steers the writing"}
Industry, audience, tone and brand voice notes go into the system prompt of
every generation step, for every blog on the site. Two blogs on one site are
written by the same in-house voice; what you control per blog is *which
topics get written there*. The example below does exactly that, and
"Steering each blog separately", below, covers how far that goes.
:::

## Worked example: a Romanian blog and an English blog

`example.com` belongs to a Romanian company that sells invoicing software.
It sells at home, where buyers care about deadlines set by the tax
authority, and it exports to Western Europe, where buyers care about
integrations and data processing agreements. Same product, two readerships
that share almost no search queries.

::::steps

:::step[Create the two blogs]
On the site page, fill **Blog name** and **Base path** and press
**Create blog**, twice:

| Name | Base path | Language |
|---|---|---|
| Blog RO | `/blog` | Română |
| Blog EN | `/en/blog` | English |

The name is internal — it labels the blog in the dashboard, in the review
queue, and in webhook payloads. The base path is what readers see.

**Language** is the one setting you cannot change later. Every step of
generation writes in it: the title, the headings, the body, the anchor text,
the meta description. The draft is checked against that language's house
style, so a Romanian article gets caught on "În era digitală" and an English
one on "In today's fast-paced world". A blog serves one language, which is
why changing it after the first article is refused: the existing posts would
stop resolving. Run a second blog instead, which is exactly what this page is
about.
:::

:::step[Mount each one on your site]
Each blog needs its own page on your domain and its own snippet. See
"Serving both blogs", below, for the routing rules — this is the step
people get wrong.
:::

:::step[Give each one its own schedule and palette]
Two audiences rarely want the same rhythm. A plausible split:

| Setting | `/blog` (RO) | `/en/blog` (EN) |
|---|---|---|
| Cadence | weekly, 1 post | biweekly, 1 post |
| Timezone | `Europe/Bucharest` | `Europe/Berlin` |
| Publish hour | 8 | 10 |
| Auto-publish | on — you read Romanian and trust the output | off — every post gets read first |
| Theme | site palette | site palette |

Both blogs can share a palette; they don't have to. If the English blog
lives on a differently-styled part of your site, give it its own fifteen
tokens on the blog page.
:::

:::step[Feed each one its own topics]
Both blogs draw briefs from their **own** queue. **Plan topics** on the
Romanian blog and on the English blog produces two independent lists, and
you approve into each separately. The next section is about what to approve.
:::

:::step[Route the output where it belongs]
Webhooks and the WordPress target are per blog, so the Romanian posts can go
to one WordPress install (or one automation) and the English posts to
another, with no filtering on your side.
:::

::::

## Different content for different audiences

The point of the split is that the two blogs don't publish translations of
each other — they publish different articles, because the readers are
looking for different things.

::::compare{title="Two readerships, one product"}

:::option[Blog RO — /blog]{key=A severity=info}

**Reader:** the owner or accountant at a 5–50 person Romanian firm, already
obliged to file electronically, comparing tools in RON.

**Publishes:** deadline explainers, step-by-step guides through local filing
rules, pricing in RON, checklists ahead of a reporting deadline.

**Links to:** `/preturi`, `/functionalitati`, the Romanian help centre.

:::

:::option[Blog EN — /en/blog]{key=B severity=success}

**Reader:** an operations or procurement lead in Germany or the Netherlands
running a vendor evaluation, who has never heard of your company.

**Publishes:** integration guides, migration-from-incumbent walkthroughs,
data-processing and GDPR questions, "how to evaluate a vendor" pieces.

**Links to:** `/pricing`, `/integrations`, the security page.

:::

::::

Concretely, the two topic queues would look nothing alike:

| `/blog` (RO) | `/en/blog` (EN) |
|---|---|
| Termenele de raportare din 2026, pe luni | How to evaluate an invoicing vendor in 30 days |
| Ce faci când o factură e respinsă | Migrating 40,000 invoices without downtime |
| Facturare pentru PFA: ce se schimbă | GDPR and invoice data: what your DPA must say |
| Cât costă, în RON, o eroare de raportare | Connecting invoicing to your ERP: three patterns |

Nothing about that table is enforced by the product — it's the editorial
call you make when you approve topics into each queue. What the product
gives you is that the two queues, schedules and review streams never touch.

:::callout{severity=info title="The planner dedupes per blog, not per site"}
Topic planning avoids repeating titles that already exist **on that blog**.
Two blogs on one site can therefore be proposed the same idea — which is
exactly what you want when one is going to write it in Romanian and the
other in English, and something to watch when both are in the same language.
:::

## Steering each blog separately

The profile that shapes *how* an article is written is per site, so per-blog
editorial direction happens through the briefs:

- **Approve selectively.** The topic queue is the real per-blog control.
  Reject anything that belongs on the other blog rather than letting it run.
- **The title and the angle are followed.** A brief you write yourself
  carries a title, a target keyword, and an angle, and the generator plans
  that exact topic's sections from them — so the language and framing of the
  brief carry into the article. In the dashboard this is the
  **Write about…** field during onboarding; over the API it's
  `POST /v1/orgs/{orgId}/blogs/{blogId}/briefs` with
  `{"title": …, "target_keyword": …, "angle": …}` (session-authenticated,
  like the rest of the dashboard API).
- **Keep language out of the brand voice notes.** They're site-wide and go
  into every prompt for every blog — "write in Romanian" there flips your
  English blog too. Put the language in the brief, where it only affects one
  post.
- **Add both language versions of your money pages as link targets.** The
  writer receives the whole site's target list with each target's anchor
  keywords, and picks contextually. Give `/preturi` Romanian anchors and
  `/pricing` English ones and the right one gets chosen; leave only the
  English page in the list and your Romanian articles will link to it.

## Serving both blogs

Each blog is a separate mount. Whichever integration you use, you do it once
per blog.

**JS SDK** — one container per page, and the SDK binds to the *first*
`[data-baas-blog]` element it finds, so the two snippets go on two different
pages:

```html
<!-- the page served at /blog -->
<div data-baas-blog data-key="pk_YOUR_KEY" data-base-path="/blog"
     data-api="https://api.dailysmith.com"></div>
<script src="https://api.dailysmith.com/v1/sdk/blog.js" async></script>
```

```html
<!-- the page served at /en/blog -->
<div data-baas-blog data-key="pk_YOUR_KEY" data-base-path="/en/blog"
     data-api="https://api.dailysmith.com"></div>
<script src="https://api.dailysmith.com/v1/sdk/blog.js" async></script>
```

Same key — it identifies the site, not the blog. Each shell must be served
for its base path *and everything under it*, e.g. on Netlify:

```
/blog/*      /blog/index.html      200
/en/blog/*   /en/blog/index.html   200
```

**Edge proxy** — one rule per base path, same key (Netlify `_redirects`
shown; the [proxy page](/docs/install-proxy) has the same rule for
Cloudflare, Vercel, nginx and Caddy):

```
/blog           https://api.dailysmith.com/v1/render/PK/blog             200
/blog/*         https://api.dailysmith.com/v1/render/PK/blog/:splat      200
/en/blog        https://api.dailysmith.com/v1/render/PK/en/blog          200
/en/blog/*      https://api.dailysmith.com/v1/render/PK/en/blog/:splat   200
```

Order the rules so the more specific path is matched — or, on a platform
that matches in file order, put `/en/blog` first.

**Static prerender** — one run per blog:

```
npx baas-blog prerender --key pk_YOUR_KEY --api https://api.dailysmith.com --base /blog --out ./public/blog
npx baas-blog prerender --key pk_YOUR_KEY --api https://api.dailysmith.com --base /en/blog --out ./public/en/blog
```

To discover the mounts programmatically — useful in a build script that
shouldn't hardcode them — ask the [delivery API](/docs/delivery-api):

```
GET /v1/delivery/{publicKey}/blogs
→ {"blogs":[{"name":"Blog RO","base_path":"/blog","default_locale":"ro"},
            {"name":"Blog EN","base_path":"/en/blog","default_locale":"en"}]}
```

## SEO across two blogs

- **`sitemap.xml` and `rss.xml` are site-wide.** One sitemap covers every
  blog's index and every published post, so you reference a single URL from
  `robots.txt` and both blogs get crawled. The flip side: the RSS feed
  interleaves both blogs — if you offer readers a feed per language, build
  it from `GET /v1/delivery/{publicKey}/posts?base_path=…` instead.
- **Canonicals are per post** and always point at the post's own path under
  its blog's base path, so the two blogs never compete for the same URL.
- **hreflang is yours to add.** Nothing links the Romanian and English blogs
  as alternates — they're separate blogs with separate posts, not
  translations of one another. If you do publish a matched pair, put the
  `<link rel="alternate" hreflang="…">` tags in your own page shell.
- **A language switcher is just a link** between `/blog` and `/en/blog`,
  placed in your site's own navigation.

:::callout{severity=warning title="Server-rendered pages declare English today"}
Every post is stored under the site's locale, which is `en` for every site
right now — the per-blog locale isn't settable yet. Two consequences for a
bilingual setup, both cosmetic to readers and visible to crawlers:

- Pages served through the [edge proxy](/docs/install-proxy) or
  [static prerender](/docs/install-prerender) carry `<html lang="en">` even
  when the article is Romanian. On the [JS SDK](/docs/install-sdk) path this
  doesn't arise — the `lang` on your own shell page is what counts, so set
  `lang="ro"` on the Romanian one.
- Don't pass `?locale=ro` to the render or delivery endpoints. It filters by
  the stored locale, so it returns an empty index rather than your Romanian
  posts. Omit the parameter and each blog serves everything it has.
:::

## When a second site is the right answer

Two blogs share one profile, one link target pool, one quota and one bill.
Reach for a second site instead when:

- **The domains differ** — `example.ro` and `example.com` are two sites by
  definition; a site is one verified domain.
- **The voice genuinely differs.** Two brands, or a consumer brand and a
  technical one, need two content profiles — and the profile is per site.
- **The money pages differ.** Link targets are per site, so two products
  with separate funnels are cleaner apart.
- **You want separate billing and separate quotas**, e.g. one site per
  client in an agency.

The cost is a second verification, a second profile scan, and a second
subscription. Within one domain, though, blogs are the only split available:
an organization can't hold two sites for the same domain.

## Limits and housekeeping

Blogs per site are part of what each plan sells — 1 on Trial and Starter, 3
on Growth, unlimited on Scale — alongside a locale allowance. As the
[plans page](/docs/billing) notes, the article quota is the limit actually
enforced today; the blog count is packaging. Quota is per site either way,
so a second blog draws from the same monthly article allowance as the first
— two blogs at four posts a month each need a plan that covers eight.

Two things about blogs are one-way today, so decide them before you press
**Create blog**:

- **The base path is fixed after creation.** The name, auto-publish and
  featured images are editable; the path isn't. Changing it means creating a
  second blog — and the posts stay with the original.
- **Blogs can't be deleted.** To retire one, pause its schedule and remove
  its mount from your site. Its posts stop being reachable as soon as the
  path stops resolving on your domain.
