Read your store from any website or app with an API key.

Introduction

Website API

The Nebular API serves your store to any website or app: products, categories, banners, careers, authors, a contact form, and a blog that ships with everything search engines and answer engines read. It is read-only except for the contact form, and every request is scoped to the store that owns the API key.

Base URL

https://api.dashify.aurbyn.com/api/v1

Auth

x-api-key header

Format

JSON, plus Markdown, RSS and XML feeds

Get started

Quickstart

  1. In the dashboard open Settings → API Integration and generate a key. Keys start with nbl_ and are shown once.
  2. Store it as an environment variable on your server, for example NEBULAR_API_KEY.
  3. Make your first request:
Shell
curl "https://api.dashify.aurbyn.com/api/v1/blogs?limit=5&fields=summary" \
  -H "x-api-key: nbl_your_key"

You should get { "success": true, "data": [...] }. From here, pick the endpoints you need in the sections below.

Get started

Authentication

Send the key on every request in the x-api-key header. Never put it in a URL or in code that ships to browsers.

HTTP
GET /api/v1/store HTTP/1.1
Host: api.dashify.aurbyn.com
x-api-key: nbl_your_key

Domain verification

Turn on Require domain verification in Settings and the API answers only requests whose Origin or Referer is a domain you have verified. Server-side calls send no Origin, so keep it off if you fetch from a backend.

Get started

Responses, errors, limits

JSON endpoints answer with one envelope. Lists add a pagination object.

Success
{ "success": true, "data": { … }, "pagination": { "page": 1, "limit": 50, "total": 24, "pages": 1 } }
Error
{ "success": false, "message": "Invalid API key" }
StatusMeaningWhat to do
200OKThe request succeeded.
401UnauthorizedMissing or invalid API key.
403ForbiddenKey inactive, or the request came from a domain you have not verified while domain verification is on.
404Not foundNo record with that id or slug, or a draft post (drafts are never public).
429Too many requestsRate limit reached. Read Retry-After and back off.
500Server errorSomething failed on our side. Retry with backoff; contact support if it persists.

The limit is 100 requests per minute per IP. Responses carry RateLimit-* headers. Cache reads on your side; blog pages change rarely and a five-minute revalidation is plenty.

Reference

Store

GET/storeStore details
Response
{ "success": true, "data": { "name": "My Store", "description": "…", "slug": "my-store", "logo": "https://…", "settings": { "currency": "USD" }, "contactInfo": { … } } }

Reference

Products and categories

GET/menuList products
Query parameterMeaning
categoryFilter by category name
searchText search over name and description
pagePage number, from 1
limitItems per page
sortByField to sort by
Response
{ "success": true, "data": [{ "_id": "…", "name": "Product", "recipe": "Description", "images": ["https://…"], "sizes": [{ "size": "M", "price": 19.99, "quantity": 12 }], "category": "Shirts", "featured": false }], "pagination": { "page": 1, "limit": 20, "total": 45, "pages": 3 } }
GET/menu/:idOne product
GET/menu/category/:nameProducts in a category

Hyphens in the name are read as spaces.

Query parameterMeaning
page—
limit—
GET/featuredFeatured products
Query parameterMeaning
limit—
GET/searchSearch products and categories
Query parameterMeaning
qSearch text
limit—
Response
{ "success": true, "data": { "menuItems": [...], "categories": [...], "total": 7 } }
GET/categoriesCategories, flat

Up to three levels: category, sub-category, sub-sub-category. Root categories have parent null.

Response
{ "success": true, "data": [{ "_id": "…", "name": "Electronics", "parent": null }, { "_id": "…", "name": "Phones", "parent": { "_id": "…", "name": "Electronics" } }] }
GET/categories/treeCategories as a tree
Response
{ "success": true, "data": [{ "_id": "…", "name": "Electronics", "children": [{ "_id": "…", "name": "Phones", "children": [] }] }] }
GET/categories/:nameOne category by name

Reference

Banners, careers, contact

GET/bannersDesktop hero banners
Response
{ "success": true, "data": [{ "_id": "…", "images": ["https://…", "https://…"] }] }
GET/banners/mobileMobile hero banners
GET/careersJob listings
Query parameterMeaning
page—
limit—
Response
{ "success": true, "data": [{ "_id": "…", "title": "Designer", "descriptionOne": "…", "descriptionTwo": "…", "image": "https://…" }], "pagination": { … } }
GET/careers/:idOne job listing
POST/contactSubmit a contact form message
Request body
{ "name": "Ada", "email": "ada@example.com", "phone": "+1 555 0100", "comment": "Hello" }
Response
{ "success": true, "message": "Message sent" }
GET/authorsBlog authors

Posts carry the author as a name string; match it with name here for the bio and photo.

Response
{ "success": true, "data": [{ "_id": "…", "name": "Clerion Research Team", "email": "…", "photoURL": "https://…", "bio": "…" }] }

Reference

Blog

Only published posts are public; drafts stay in the dashboard. Every post, including ones written before these fields existed, is served with a slug, an excerpt, meta fields, reading time and structured data, derived on the fly when the author left them out.

GET/blogsPublished posts, newest first
Query parameterMeaning
tagsComma-separated tags
categoryOne category
page—
limitUp to 200
fields"summary" leaves the HTML body out
Response
{ "success": true, "data": [{ "_id": "…", "title": "…", "slug": "how-to-track-chatgpt-traffic", "url": "https://yoursite.com/blog/how-to-track-chatgpt-traffic", "excerpt": "…", "metaTitle": "…", "metaDescription": "…", "category": "AI traffic", "tags": ["ai traffic"], "author": "…", "image": "https://…", "imageAlt": "…", "publishedAt": "2026-10-01T…", "updatedAt": "2026-10-03T…", "readingTimeMinutes": 6, "wordCount": 1180, "content": "<p>…</p>" }], "pagination": { "page": 1, "limit": 50, "total": 24, "pages": 1 } }
GET/blogs/:idOne post by id
Response
{ "success": true, "data": { …post fields…, "seo": { …see "SEO and GEO fields"… }, "jsonLd": { "@context": "https://schema.org", "@graph": [ … ] }, "markdown": "## Why it matters\n…" } }
GET/blogs/slug/:slugOne post by slug

Legacy posts answer to "<kebab-title>-<id>" as well.

GET/blogs/:id?format=mdThe post as a Markdown document

Or send Accept: text/markdown. Front matter carries every SEO field.

GET/blogs/llms.txtIndex for answer engines

Serve it at your site's /llms.txt.

GET/blogs/llms-full.txtEvery post as Markdown in one file
GET/blogs/rss.xmlRSS 2.0 with full content
GET/blogs/sitemap.xmlSitemap entries with lastmod

Blog

SEO and GEO fields

Single-post responses include a seo object. It is everything a page needs to rank in search and to be quoted by ChatGPT, Perplexity, Google AI Overviews and Claude, so your site renders it instead of computing it.

FieldWhat it is
slugURL slug. New posts carry a real one; legacy posts derive "<kebab-title>-<id>".
url, canonicalPublic URL built from your post URL template (Settings → API Integration). null until the template is set.
metaTitleFor the <title> tag. 30 to 60 characters; derived from the title when missing.
metaDescriptionFor the description tag. 70 to 155 characters; derived from the excerpt when missing.
excerptA 1 to 3 sentence direct answer to the title. Print it under the H1; answer engines quote it.
category, tags, targetKeywordClassification and the phrase the post targets.
author{ name, bio, photoURL } from the Authors list when the name matches.
image, imageAltCover image and its alt text.
publishedAt, updatedAtUse for datePublished and dateModified, and for sitemap lastmod.
wordCount, readingTimeMinutesReading time at 200 words per minute.
toc[{ id, text }] for every H2, ids matching a lowercase-hyphen slug of the heading.
faqs[{ question, answer }]. Rendered as FAQPage schema in jsonLd.
sources[{ title, url }] cited pages. Rendered as citation in jsonLd.
openGraph, twitterReady-made card objects.
jsonLdA schema.org @graph with BlogPosting (and FAQPage when there are FAQs). Embed it as application/ld+json.

Set your post URL template

URLs in url, canonical, llms.txt, RSS and the sitemap come from the template in Settings → API Integration → Claude connector, for example https://example.com/blog/{slug}.

Recommended rendering

  • seo.metaTitle and seo.metaDescription in the head, with seo.canonical.
  • jsonLd in a <script type="application/ld+json">.
  • seo.excerpt as the first paragraph under the H1.
  • seo.toc as a contents list; the ids match the H2s in content.

Blog

Markdown and feeds

Markdown is the native format of AI agents and static site builds. Any post comes back as one Markdown document with YAML front matter, and the dashboard imports the same format through Blogs → Import .md.

Markdown
---
title: "How to track ChatGPT traffic to your website"
slug: "how-to-track-chatgpt-traffic"
metaTitle: "How to track ChatGPT traffic to your website"
metaDescription: "What assistant referrers look like and which tools show them as their own channel."
excerpt: "You can see ChatGPT traffic by grouping referrers from chatgpt.com into their own channel."
author: "Clerion Research Team"
category: "AI traffic"
tags: ["ai traffic", "chatgpt"]
image: "https://i.ibb.co/…/cover.png"
imageAlt: "A referrer table with assistant domains grouped"
targetKeyword: "track ChatGPT traffic"
---

## Why it matters

Assistant referrals are a growing channel…

## Frequently asked questions

### Does ChatGPT send a referrer?

Yes, in most browsers links clicked inside ChatGPT carry chatgpt.com as the referrer.

## Sources

- [OpenAI crawler documentation](https://platform.openai.com/docs/bots)

Front matter aliases accepted on import:

You writeNebular reads it as
descriptionmetaDescription
tldr, summaryexcerpt
keywordstags
cover, coverAltimage, imageAlt
keywordtargetKeyword
topiccategory

Feeds to serve from your own domain

Your pathProxy to
/llms.txthttps://api.dashify.aurbyn.com/api/v1/blogs/llms.txt
/llms-full.txthttps://api.dashify.aurbyn.com/api/v1/blogs/llms-full.txt
/rss.xmlhttps://api.dashify.aurbyn.com/api/v1/blogs/rss.xml
/sitemap-blog.xmlhttps://api.dashify.aurbyn.com/api/v1/blogs/sitemap.xml

Guides

Code examples

nextjs
// app/blog/[slug]/page.tsx
const API = "https://api.dashify.aurbyn.com/api/v1";
const headers = { "x-api-key": process.env.NEBULAR_API_KEY! };

export async function generateMetadata({ params }) {
  const { data } = await fetch(`${API}/blogs/slug/${params.slug}`, { headers, next: { revalidate: 300 } }).then((r) => r.json());
  return {
    title: data.seo.metaTitle,
    description: data.seo.metaDescription,
    alternates: { canonical: `/blog/${data.slug}` },
    openGraph: data.seo.openGraph,
    twitter: data.seo.twitter,
  };
}

export default async function Post({ params }) {
  const { data } = await fetch(`${API}/blogs/slug/${params.slug}`, { headers, next: { revalidate: 300 } }).then((r) => r.json());
  return (
    <article>
      <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(data.jsonLd) }} />
      <h1>{data.title}</h1>
      <p className="excerpt">{data.seo.excerpt}</p>
      <div dangerouslySetInnerHTML={{ __html: data.content }} />
    </article>
  );
}

// app/llms.txt/route.ts
export async function GET() {
  const text = await fetch(`${API}/blogs/llms.txt`, { headers, next: { revalidate: 300 } }).then((r) => r.text());
  return new Response(text, { headers: { "content-type": "text/plain; charset=utf-8" } });
}

Guides

Prompt for your AI assistant

Pick what you are building and paste the prompt into Claude, ChatGPT or Cursor. It carries the exact endpoints and shapes.

Prompt
Integrate the Nebular public API into Next.js. Produce production-ready code.

## API
Base URL: https://api.dashify.aurbyn.com/api/v1
Auth: header x-api-key: <key> (keys start with nbl_). Keep the key server-side or in an environment variable.
Every response: { success: boolean, data: any, message?: string }. Errors: 401 bad key, 403 domain not verified, 404 not found, 429 rate limit (100 requests/minute per IP), 500 server error.

## Endpoints to use
### Blog with SEO
- GET /blogs?fields=summary → items carry slug, url, excerpt, metaTitle, metaDescription, category, publishedAt, updatedAt, readingTimeMinutes
- GET /blogs/slug/:slug → post + seo { metaTitle, metaDescription, excerpt, toc, faqs, sources, openGraph, twitter, jsonLd } + markdown
- Feeds to proxy: GET /blogs/llms.txt, /blogs/llms-full.txt, /blogs/rss.xml, /blogs/sitemap.xml
- Render seo.metaTitle/metaDescription in <head>, embed jsonLd as application/ld+json, print seo.excerpt under the H1.

## Requirements
- Handle loading, empty and error states.
- Cache reads for a few minutes where the framework allows.
- For blog pages, use the seo block for <head> tags and embed jsonLd; never recompute what the API already derives.
- Comment each API call briefly.

Guides

Security

  • Call the API from your server, or proxy it, so the key never reaches a browser.
  • Use one key per site and revoke keys you no longer use under Settings → API Integration.
  • Turn on domain verification when you must call from the browser.
  • Rotate keys if one appears in a repository, a log or a support ticket.