Create Bitly Pages
A Bitly Page is a hosted landing page that collects your links, social profiles, and other content at a single short URL. You can create Pages, fill them with content blocks, style them, and publish them entirely through the API.
Refer to the Create Endpoint for a detailed breakdown of every field available on the create endpoint. Some Page features, such as grids and carousels, depend on your subscription plan.
Drafts and Publishing
Every Page has two versions: a draft and a live version. This is the most important thing to understand before you start.
Most endpoints that change a Page — creating content blocks, updating appearance, changing the display name or description — write to the draft. Those changes are not visible to visitors until you publish. Publishing copies the draft over the live version in a single step.
Two kinds of change skip the draft and take effect on the live Page immediately, with no publish required:
- Changing the
uriwithPATCH /v4/sites/{site_id}. The Page starts serving at its new URL right away. - Changing the Page's status, such as deactivating it with
DELETE /v4/sites/{site_id}.
When a
PATCHchanges theuri, thedisplay_nameanddescriptionsent in that same request are written to the live Page as well, because the URL, status, and content are saved together. Send those fields in their own request if you want them to stay in the draft until you publish.
The is_edited field on a Page tells you whether the draft has unpublished changes. There is no need to publish after every change — the rest of this tutorial creates a Page, adds blocks to it, and sets its appearance, then publishes once at the end.
Creating a Page
The create endpoint takes three fields in total, and no styling or content options. A Page is always created empty, using Bitly's default theme and layout, and is then filled in through the content and appearance endpoints described below. Of the three fields, group_guid and uri are required:
POST /v4/sites
{
"group_guid": "Ba1bc23dE4F",
"uri": "bit.ly/m/mypage",
"display_name": "My Page"
}
group_guid string The group the Page belongs to. Required.
uri string
The full short URL for the Page. Required, and always in the form domain/m/keyword. The middle segment is a literal m rather than a path of your choosing — it is the only value accepted. A missing uri, or one with a different middle segment or any number of segments other than three, is rejected with INVALID_URI.
The domain must be bit.ly or a custom domain your group has access to. Any other domain returns INVALID_SITE_DOMAIN.
The keyword can be up to 255 characters and is limited to letters, numbers, dashes, and underscores. Spaces, other punctuation, currency and math symbols, emoji, and invisible characters such as zero-width spaces are all rejected. An invalid keyword returns INVALID_SITE_KEYWORD, both when creating a Page and when changing the uri on an existing one.
display_name string
The title shown at the top of the Page. Optional; defaults to Welcome! when omitted.
A successful response returns 201 with the new Page:
{
"site_guid": "Ma1bc23dE4F",
"group_guid": "Ba1bc23dE4F",
"status": "new",
"is_edited": false,
"url": "https://bit.ly/m/mypage",
"content": {
"display_name": "My Page",
"description": ""
},
"appearance": { },
"button_count": 0,
"created": "2024-01-15T10:00:00+0000",
"modified": "2024-01-15T10:00:00+0000",
"last_published": "1970-01-01T00:00:00+0000",
"redirects": []
}
Your plan caps how many Pages you can have at once. This is a total allocation, not a monthly quota: it counts every Page across all Groups in your organization, and it does not reset at the start of the month.
Deleting a Page frees up a slot. Creating a Page while you are already at the limit returns a 402 error. To create more Pages, delete an existing one or upgrade your plan.
If the domain and keyword are already taken, the request returns 400 with SITE_DOMAIN_AND_KEYWORD_TAKEN.
Starting From a Template
Rather than styling a new Page field by field, you can apply one of Bitly's templates to it:
POST /v4/sites/Ma1bc23dE4F/template
{
"template_guid": "Ta1bc23dE4F"
}
template_guid string
The template to apply. Required, and rejected with 400 and INVALID_TEMPLATE_GUID if it is not a well-formed template ID. The available templates come from GET /v4/site_templates, covered under Customizing Appearance below.
A successful response returns 200 with the updated Page.
Apply a template while the Page is still empty. The template replaces the Page's appearance,
display_name, anddescriptionoutright, but its sample blocks are appended to whatever blocks are already there rather than replacing them. Applying a template to a Page you have already built leaves you with both sets of blocks and the template's title.
The sample blocks land after your existing blocks in sort order, and a template's social block is skipped rather than duplicated if the Page already has a block for that channel. Applying a template writes to the draft, so the change is not live until you publish.
Adding Content Blocks
Content on a Page is made up of blocks. Each block is created against the Page:
POST /v4/sites/{site_id}/blocks
You must specify the block type in the request. The available type values are:
bitlink— a button that links to one of your Bitly linkssocial— a social media iconyoutubeVideo— an embedded YouTube videoimage— an image cardContact info— a digital business cardtext_block— a block of formatted text
Every block request uses the same request body structure. The content object is what varies by type.
content object The block's data. Required when creating a block.
schedule_start / schedule_end string RFC3339 timestamps that control when the block is visible. A start date in the future makes the block inactive until then.
is_active boolean Whether the block is currently shown.
is_pinned boolean Whether the block is pinned to the top of the Page.
parent string The block ID of the container this block belongs to. Omit for top-level blocks.
Link Blocks
A bitlink block points at an existing Bitly link. Create the link first with the Shorten & Customize Links flow, then attach it:
POST /v4/sites/Ma1bc23dE4F/blocks
{
"type": “bitlink”,
"content": {
"bitlink_id": "bit.ly/abc123",
"link_title": "Read our latest post",
"description": "Published this morning"
}
}
bitlink_id string The Bitly link to open. Required.
link_title string The button label. Required, up to 255 characters.
description string Optional supporting text, up to 255 characters.
The Bitly link must belong to the same group as the Page. Linking to a bitlink from another group returns
400. Attaching the same bitlink to two blocks on one Page also returns400.
Social Blocks
A social block renders an icon for one social channel:
POST /v4/sites/Ma1bc23dE4F/contents/social
{
"type": “social”,
"content": {
"channel": "instagram",
"url": "https://www.instagram.com/bitly/"
}
}
The supported channel values are facebook, twitter, youtube, tiktok, instagram, whatsapp, linkedin, appleMusic, discord, email, mastodon, pinterest, reddit, snapchat, soundcloud, spotify, telegram, threads, twitch, and bluesky.
The url is validated against the channel — each channel accepts only its own known domains, so an Instagram profile URL on a twitter block returns 400. The email channel is the exception: it is not parsed as a URL or checked against any domain, and url must instead be a plain email address such as you@example.com (no mailto: prefix). An invalid address returns 400 with INVALID_EMAIL. You can only add one block per channel; a duplicate returns 400 with DUPLICATE_SOCIAL_CHANNEL.
Text Blocks
A text_block holds free text and is the only block type that takes its own appearance object:
POST /v4/sites/Ma1bc23dE4F/contents/text_block
{
"type": “text_block”,
"content": {
"text": "Thanks for stopping by."
},
"appearance": {
"alignment": "center",
"font_size": "large",
"text_color": "#042F86",
"background_color": "#FFFFFF"
}
}
text string The text to display. Required, up to 500 characters.
alignment string
One of left, center, right, or justified. Defaults to center.
font_size string
One of small, medium, large, or extra_large. Defaults to medium.
text_color / background_color string Hex color codes. Optional with no default — omitted colors inherit the Page's text color and leave the block background transparent.
Block Responses
Creating a block returns 201 with the stored block:
{
"site_id": "Ma1bc23dE4F",
"block_id": "La1bc23dE4F",
"type": "bitlink",
"content": {
"bitlink_id": "bit.ly/abc123",
"link_title": "Read our latest post",
"description": "Published this morning"
},
"sort_order": 0,
"is_active": true,
"is_pinned": false,
"container_id": "root",
"is_sample": false
}
Use the returned block_id to update the block later:
PATCH /v4/sites/{site_id}/blocks/{block_id}
Fields you omit keep their stored values, so a content-only update will not change the block's active or pinned state.
You cannot set
is_activeand a schedule in the same request. Doing so returns400.
Grouping Blocks Into Containers
Blocks can be arranged into a grid or a carousel. Create the container first:
POST /v4/sites/Ma1bc23dE4F/containers
{
"type": "grid"
}
The type must be grid or carousel. Both are plan-gated features — if your account does not have access, the request returns 402.
The response is a block, and its block_id becomes the parent for blocks you want inside it:
POST /v4/sites/Ma1bc23dE4F/contents/image
{
"content": {
"image_url": "https://example.com/photo.jpg",
"link_title": "Our storefront"
},
"parent": "La1bc23dE4F"
}
Customizing Appearance
A Page's colors, fonts, layout, and header are set through a single endpoint:
PUT /v4/sites/{site_id}/appearance
PUT /v4/sites/Ma1bc23dE4F/appearance
{
"theme_id": 0,
"style_preference": "custom",
"font": "Inter",
"background_color": "#C696EE",
"text_color": "#042F86",
"description_color": "#042F86",
"default_button_background_color": "#EF8000",
"default_button_text_color": "#FFFFFF",
"hide_bitly_logo": false,
"header_appearance": {
"title_size": "large",
"title_alignment": "center",
"profile_image_shape": "circle"
}
}
This endpoint replaces the entire appearance rather than merging. Send the complete appearance object, including the values you want to keep, or omitted fields will be reset.
Use style_preference to choose between a preset theme and your own colors. When theme_id is 0, style_preference must be custom, otherwise the request returns 400.
A successful update returns 204. To see the available presets:
GET /v4/site_templates
Each template comes back with a complete appearance object, so you can use one as the starting point for your own values. Pass the optional category query parameter — Link-in-bio, Products and services, Promotions, Digital business card, Image gallery, or Recommended — to narrow the list:
GET /v4/site_templates?category=Link-in-bio
The response is a 200 with an array of templates, ordered with the free ones first:
[
{
"template_guid": "Ta1bc23dE4F",
"content": {
"display_name": "Welcome!",
"description": ""
},
"appearance": {
"theme_id": 12,
"style_preference": "theme",
"font": "Inter",
"background_color": "#C696EE",
"text_color": "#042F86",
"description_color": "#042F86",
"default_button_background_color": "#EF8000",
"default_button_text_color": "#FFFFFF",
"hide_bitly_logo": false,
"header_appearance": { }
},
"blocks": [ ],
"categories": ["Link-in-bio"],
"created": "2024-01-15T10:00:00+0000",
"modified": "2024-01-15T10:00:00+0000",
"is_active": true,
"is_paid": false
}
]
The is_paid flag tells you whether a template requires a paid plan. Copy the fields you want out of appearance and send them to PUT /v4/sites/{site_id}/appearance — remembering that it replaces the whole object, so send the complete set of values you want the Page to have.
Publishing
None of the changes above are live until you publish:
POST /v4/sites/{site_id}/publish
POST /v4/sites/Ma1bc23dE4F/publish
No request body is required. A successful response returns 200 with the published Page, including an updated last_published timestamp.
Requirements and constraints:
- The Page must exist and be accessible. If the
site_idis not found or you do not have access to it, the endpoint returns404. - The Page must have unpublished draft changes. Publishing a Page with nothing to publish returns
400. - Pages have a maximum number of links. Exceeding it returns
400withLINK_CAPACITY_EXCEEDED.
To throw away draft changes instead of publishing them:
DELETE /v4/sites/{site_id}/draft
This returns 204 and reverts the draft to match the live Page.
Updating a Page
Renaming a Page or moving it to a new URL is done on the Page itself rather than through the appearance endpoint:
PATCH /v4/sites/Ma1bc23dE4F
{
"uri": "bit.ly/m/newpage",
"display_name": "My Renamed Page",
"description": "Everything in one place"
}
Changing the uri automatically creates a redirect from the old URL, so existing links keep working. The redirects appear in the redirects array when you fetch the Page.
As described above, a uri change applies to the live Page immediately rather than waiting for a publish, and it carries the display_name and description from the same request along with it. A request that changes only the name or description updates the draft alone.
Sending an empty string for display_name or description clears the stored value, while omitting the field entirely leaves it unchanged.
You can also attach a QR Code to a Page with qr_code_id, but only if one is not already attached — otherwise the request returns 400 with SITE_ALREADY_ATTACHED_TO_QR_CODE.
Retrieving Pages
To fetch a single Page:
GET /v4/sites/{site_id}
To list every Page in a group:
GET /v4/groups/{group_guid}/sites
This endpoint is paginated. Pass size to control the page length — it defaults to 50 and cannot exceed 100 — and follow the pagination.next URL, or pass the pagination.search_after value back as search_after, to walk through the results. You can also narrow the list to a specific URL with the sites_url_param query parameter.
Deleting a Page
DELETE /v4/sites/{site_id}
This deactivates the Page and returns 204. Because it is a status change, it applies to the live Page immediately — the Page stops resolving for visitors without a publish.