Machine-readable version: https://bazzly.ai/docs/api.md

Bazzly REST API

Bazzly finds Reddit posts where a product fits, drafts replies and DMs, posts comments through warmed-up accounts, and tracks everything in queues. This API exposes the same actions as the dashboard. It is designed to be driven by AI agents: every list returns plain JSON, every credit-spending action previews its cost before it charges, and the same key also works with the Bazzly MCP server at https://api.bazzly.ai/mcp.

  • OpenAPI 3.1 specification: https://bazzly.ai/openapi.json
  • Base URL: https://api.bazzly.ai/public/v1
  • Auth: Authorization: Bearer <api key> on every request (X-Bazzly-Api-Key: <api key> also works)
  • Format: JSON in, JSON out. Send Content-Type: application/json on POST, PATCH and PUT.
  • Keys: created under Account Settings → API & MCP, available during the free trial. Each key has scopes: read, write, top-ups.
  • Errors: standard HTTP codes with a JSON body { "statusCode", "message" }. 401 missing or invalid key, 403 missing scope, 404 not found, 400 validation error (the message names the field), 429 a limit was hit: 120 requests per minute per key, or an order cap.
  • Access: keys work during the free trial. A cancelled subscription loses API and MCP access when its paid period ends (401).

Quick start

  1. GET /me to confirm the key and see its scopes.
  2. GET /projects to find the project id.
  3. GET /opportunities?filter=top-ranked&sort=score&limit=10 to see the best Reddit posts to reply to.
  4. POST /opportunities/{id}/generate-reply with { "confirmCreditSpend": true } to draft a comment (0.1 credits).
  5. POST /queue/reply with the opportunity id, deliveryMethod and the text to post it (10 credits via a warmed-up account, free from the user's own account).
  6. GET /queue/reply to watch it go live.

Credits and confirmation

Actions that spend credits or money return a preview instead of acting until you confirm:

  • Credit-spending calls (generate-reply, generate-dm, POST /queue/reply with deliveryMethod: "high_karma", POST /custom-tasks) return { "requiresConfirmation": true, "costs": { ... }, "message": "..." } when called without "confirmCreditSpend": true. Repeat the call with the flag to perform it.
  • Money-spending calls (POST /projects/{id}/activate, POST /projects/{id}/credits/manual) use "confirmPaymentSpend": true the same way.

Costs: a warmed-account comment is 10 credits, an upvote 0.2 credits, an AI draft (reply or DM) 0.1 credits. Comments posted from the user's own account through the browser extension are free.

Account

GET /me

Scope: read. Returns the account and the key in use: { id, email, name, apiKey: { id, name, scopes } }.

Projects

A project is one product: its profile, watched subreddits, Google searches, filters, DM template and credits. Most accounts have one.

GET /projects

Scope: read. Returns { data: Project[] }. Project fields include id, product_name, product_url, product_description, status (active, draft, inactive, locked), icp, keywords, competitors, pain_points, key_benefits, key_features, watched_subreddits, dm_template, discover_score_threshold, discover_classifications, scoring_custom_instructions, reply_custom_instructions.

GET /projects/{projectId}

Scope: read. One project with the fields above.

POST /projects

Scope: write. Body { "url": "https://yourproduct.com" }. Bazzly analyzes the page and creates the project with a generated profile. The first project on an account is created active; later ones are created as draft and need activate. At most two drafts can exist at once.

PATCH /projects/{projectId}

Scope: write. Partial update of the profile. Accepted fields and limits:

FieldTypeLimits
product_namestringrequired non-empty if sent
product_urlstringvalid URL
product_descriptionstring70 to 500 characters
icpstring20 to 400 characters
keywordsstring[]up to 10, 50 chars each
competitorsstring[]up to 6, 50 chars each
pain_pointsstring[]up to 5, 120 chars each
key_benefitsstring[]up to 5, 120 chars each
key_featuresstring[]up to 6, 120 chars each
watched_subredditsstring[]up to 10, with or without the r/ prefix
dm_templatestring3 to 500 chars; variables allowed: {post_problem_summary}, {product_benefit}
discover_score_thresholdinteger0 to 100, minimum fit score for opportunities
discover_classificationsstring[] or nullwhich post classifications to surface
scoring_custom_instructionsstring or nullup to 1500 chars
reply_custom_instructionsstring or nullup to 1500 chars, guides AI reply drafts

Unknown fields are rejected. Autopilot settings are not exposed here.

POST /projects/{projectId}/activate

Scope: top-ups. Activates a draft or inactive project. Body { "confirmPaymentSpend": true, "profile": { ...same fields as PATCH... } }. Without the flag it returns { requiresConfirmation: true, ... } describing the prorated seat charge, if any. Additional active projects are billed as extra seats at the plan price.

GET /projects/{projectId}/credits

Scope: read. Returns { balance: { monthly, topped_up, total } }. Monthly credits reset each billing period; topped-up credits never expire.

POST /projects/{projectId}/credits/manual

Scope: top-ups. Body { "amount": 50, "confirmPaymentSpend": true }. Charges the card on file at 1 credit = 1 USD; amount is 5 to 1000. Without the flag it returns a preview.

GET /projects/{projectId}/credits/auto

Scope: top-ups. Returns { auto_topup_enabled, auto_topup_threshold, auto_topup_amount }.

PUT /projects/{projectId}/credits/auto

Scope: top-ups. Body { "auto_topup_enabled": true, "auto_topup_threshold": 20, "auto_topup_amount": 50 }. Threshold 1 to 100, amount 25 to 250. Both are required when enabling. Auto top-up only fires while Autopilot is running.

Opportunities

An opportunity is a Reddit post scored against the project. Ids are prefixed: ro-{uuid} for a post found by subreddit monitoring ("recent"), sp-{uuid} for a post that ranks on Google for one of the project searches ("top-ranked"). Both kinds share the endpoints below.

GET /opportunities

Scope: read. Query: projectId (uuid, optional), filter (all, recent, top-ranked; default all), sort (score, time; default score), limit (1 to 100, default 50), offset. Returns { data: Opportunity[], total, nextOffset }. Each item includes the prefixed id, source, projectId, the post (title, body, subreddit, author, url, score, numComments, createdUtc), the match (fitScore 0 to 1, fitReason, classification), any proposedReply, and for top-ranked posts serpQuery, serpPosition and estimatedTraffic.

GET /opportunities/{id}

Scope: read. One opportunity.

PATCH /opportunities/{id}

Scope: write. Body { "proposed_reply": "..." } (3 to 4000 chars, or an empty string to clear) and/or { "skip": true } to dismiss it.

POST /opportunities/{id}/generate-reply

Scope: write. Body { "confirmCreditSpend": true }. Drafts a comment that answers the post and mentions the product where it fits, using the project's reply instructions. Returns { success, text, creditsSpent: 0.1 }. Without the flag: a cost preview.

Leads

A lead is a Reddit user who wrote a post that matched the project. DMs are sent from the user's own Reddit account by the Bazzly browser extension; the API queues them, it does not send them.

GET /leads

Scope: read. Query: projectId, sort (score, time), limit (1 to 100, default 50), offset. Returns { data: Lead[], total, nextOffset }. Each lead has id, projectId, redditUsername, bestFitScore, proposedDm, and reasons[] with the matching posts and fit reasons.

GET /leads/{id}

Scope: read. One lead.

PATCH /leads/{id}

Scope: write. Body { "proposed_dm": "..." } (3 to 2000 chars) and/or { "skip": true }.

POST /leads/{id}/generate-dm

Scope: write. Body { "confirmCreditSpend": true }. Fills the project DM template from the lead's posts. Returns { success, text, creditsSpent: 0.1 }.

Reply queue

Every requested comment. Own-account comments post when the user runs the browser extension; warmed-account comments post automatically within a few hours.

GET /queue/reply

Scope: read. Query: projectId, status (queued, posted, removed, cancelled; omit for all), limit (1 to 200, default 100), offset. Returns { data: ReplyQueueItem[], total, nextOffset } with id, status, deliveryMethod (extension or high_karma), content, redditUrl, sentAt, errorMessage, the source opportunity and any upvote order.

POST /queue/reply

Scope: write. Body:

{
  "opportunityId": "ro-...",
  "deliveryMethod": "high_karma",
  "content": "The comment text (3 to 4000 chars). Optional if a proposed_reply is already saved.",
  "upvotes": { "quantity": 5, "mode": "rank_first" },
  "confirmCreditSpend": true
}

deliveryMethod is high_karma (posted through a warmed-up account, 10 credits) or extension (queued for the user's own account, free). upvotes is optional: quantity 1 to 5, mode custom or rank_first (add enough to beat the current top comment). Without confirmCreditSpend the call returns { requiresConfirmation: true, costs: { highKarmaComment, upvotes, total } }. On success: { success: true, externalTaskId | outreachQueueId, creditsSpent }.

Rules the server enforces: warmed-account comments cannot contain links; strict-on-promotion subreddits may reject warmed-account posting unless the post is asking for tools; at most 10 warmed-account orders per project per day.

POST /queue/reply/{id}/cancel

Scope: write. Cancels a queued own-account comment. Warmed-account comments cannot be retracted once queued.

POST /queue/reply/{id}/retry

Scope: write. Requeues a removed or cancelled comment. Requeuing a warmed-account comment costs 10 credits again and may require edited text.

DM queue

GET /queue/dm

Scope: read. Query: projectId, status (queued, sent, failed), limit, offset. Returns { data: DmQueueItem[], total, nextOffset }.

POST /queue/dm

Scope: write. Body { "source": "lead", "sourceId": "<lead id>" } or { "source": "opportunity", "sourceId": "ro-..." } (top-ranked posts cannot be DMed). Uses the saved proposed_dm; generate or PATCH it first. Queueing is free; the DM is sent when the user runs the extension.

POST /queue/dm/{id}/cancel

Scope: write.

POST /queue/dm/{id}/retry

Scope: write. Requeues a failed DM.

Custom tasks

POST /custom-tasks

Scope: write. Comment on any Reddit post or comment by URL through a warmed-up account, or order upvotes only.

{
  "projectId": "<uuid>",
  "redditUrl": "https://www.reddit.com/r/SaaS/comments/.../",
  "content": "Optional comment text (3 to 4000 chars). Omit to order upvotes only.",
  "upvotes": { "quantity": 3, "mode": "custom" },
  "confirmCreditSpend": true
}

Same confirmation and cost rules as POST /queue/reply. Links in content are rejected.

Help center

Answer product questions from the same articles the help center renders, instead of guessing or sending the user to support.

GET /help/search

Scope: read. Query: q (2 to 200 chars), limit (1 to 10, default 5). Returns { query, results: [{ slug, title, excerpt, category, url, score, snippet }], helpCenterUrl }. The snippet is the most relevant paragraph, often enough to answer from.

GET /help/articles

Scope: read. Lists every article (slug, title, excerpt, category, url, updatedAt).

GET /help/articles/{slug}

Scope: read. One article with its full markdown body.

Feedback

POST /feedback

Scope: read (any key). Report a bug, a missing capability, a docs gap or an unanswered question on the user's behalf. Do this yourself when something blocks you; the team reads every submission and no human needs to write to support.

{
  "category": "bug | missing_feature | docs | question | other",
  "message": "What happened, what you expected (3 to 4000 chars)",
  "projectId": "<uuid, optional>",
  "context": { "endpoint": "POST /queue/reply", "status": 400, "note": "optional free-form details" }
}

Returns { id, receivedAt, message }.

MCP

The same capabilities are available as MCP tools at https://api.bazzly.ai/mcp (Streamable HTTP, bearer auth with the same key). Setup for Claude Code, Codex, Cursor and other clients: https://bazzly.ai/mcp

Support

support@bazzly.ai