# Blooms by the Box Flower Scraper (`muhammadafzal/blooms-by-the-box-flower-scraper`) Actor

Scrape Blooms by the Box wholesale flower prices, quantity tiers, stock, pack sizes and product details for florist sourcing and wedding budgets.

- **URL**: https://apify.com/muhammadafzal/blooms-by-the-box-flower-scraper.md
- **Developed by:** [Muhammad Afzal](https://apify.com/muhammadafzal) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 wholesale flower products

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Blooms by the Box Flower Scraper

Extract public wholesale flower products from Blooms by the Box into an Apify dataset for florist sourcing, wedding budgeting, catalog analysis, and repeatable price comparisons. Supply flower category URLs or individual product URLs. Each result contains the source product ID and URL, product name, purchase unit, quantity price tiers, stock status, flower details, description, image links, and extraction time.

### What it extracts

| Fields | Meaning |
| --- | --- |
| `productId`, `name`, `url`, `category` | Source SKU, product name, canonical fetched URL, and breadcrumb category |
| `unitPrice`, `lowestUnitPrice`, `unit`, `currency` | First displayed quantity-tier price, lowest quantity-tier price, sale unit, and stated currency |
| `packPrice`, `pricingTiers` | Source Offer microdata price and every displayed quantity break |
| `availability`, `inStock` | Explicit source stock status; unknown stock stays null |
| `minimumOrderQuantity`, `orderMultiple`, `purchaseText` | First tier minimum and explicit purchase instructions |
| `stemsPerBunch`, `flowersPerStem`, `size` | Source flower specifications, when stated |
| `seasonalAvailability`, `vaseLife` | Seasonal selling period and vase-life estimate |
| `description`, `imageUrls`, `scrapedAt` | Plain text description, unique product image URLs, UTC extraction timestamp |

Unit prices and pack prices are different. For example, a minimum bunch may cost $89.25 while the first tier is $3.57 per stem, with a lower $3.44 tier for larger quantities. Compare prices only when both purchase units and quantities match. `lowestUnitPrice` is a bulk discount that can require a much larger purchase. `packPrice` preserves the product detail page's Offer price without inferring a conversion; consult `purchaseText` and `pricingTiers` to interpret it. Delivery, tax, coupons, checkout totals, and delivery-date-specific inventory are outside the extraction contract.

Missing source fields are null. A rose, greenery bunch, and mixed wedding pack may have different populated attributes. Image files are not downloaded or redistributed. Stock and prices are snapshots and can change after extraction. Descriptions represent the retailer's claims, including its vase-life estimates.

### Inputs and working examples

| Input | Default | Limits and behavior |
| --- | --- | --- |
| `startUrls` | In-stock roses category | 1–10 public category/product URL objects, each with a `url` field |
| `maxResults` | 10 | 1–100 unique products across every URL; free-plan runs at most 5 |
| `keyword` | Empty | Case-insensitive product-name substring within supplied URLs; at most 100 characters |
| `inStockOnly` | false | true accepts only explicit `InStock`; unknown and preorder are excluded |
| `maxPages` | 5 | 1–10 total catalog pages, including public next links; product details are separate |
| `maxRuntimeSecs` | 180 | 15–240 scraping seconds before finalization |

The default input is:

```json
{
  "startUrls": [{ "url": "https://www.bloomsbythebox.com/in-stock-roses/" }],
  "maxResults": 10,
  "maxPages": 5,
  "keyword": "",
  "inStockOnly": false,
  "maxRuntimeSecs": 180
}
```

For white roses, use the same category with `"keyword": "White"` and optionally `"inStockOnly": true`. Keywords filter product names in your supplied categories; they do not search the entire website or inspect descriptive text. To inspect a single product:

```json
{
  "startUrls": [{ "url": "https://www.bloomsbythebox.com/in-stock-roses/rose-freedom-red-50cm_465/" }],
  "maxResults": 1
}
```

Color categories, greenery, bulk packs, and other public catalog categories use the same URL mode. The Actor follows observed public next-page links when present. Many categories expose every product in one response, so it does not invent page numbers or call disallowed internal filter endpoints. To inspect several categories, provide their public URLs and increase `maxPages` sufficiently. Results are processed in start-URL and source-page order, with detail requests kept sequential to reduce load.

In the Apify Console, enter the JSON, set a maximum total charge, and start a run. Products appear in the default dataset, exportable as JSON, CSV, or Excel. `OUTPUT` in the default key-value store records the outcome, caps, warnings, request/page counts, delivered products, result events, and stop reason. The output panel links to both products and the summary.

API users can POST this input to `https://api.apify.com/v2/acts/muhammadafzal~blooms-by-the-box-flower-scraper/runs` with an Authorization bearer header, then read the run's `defaultDatasetId` and `defaultKeyValueStoreId`. Set `build=latest`, `memory=256`, `timeout=300`, and an appropriate `maxTotalChargeUsd` query parameter. Keep the token in your own secret manager. Apify MCP users can call this Actor using its technical identifier and the same input, then fetch its dataset and `OUTPUT`; this is a batch Actor, not a Standby server.

### Output example

An abbreviated real source snapshot illustrates price semantics:

```json
{
  "productId": "465",
  "name": "Rose Freedom Red 50 Cm",
  "url": "https://www.bloomsbythebox.com/in-stock-roses/rose-freedom-red-50cm_465/",
  "category": "Roses - In Stock",
  "unitPrice": 3.57,
  "lowestUnitPrice": 3.44,
  "unit": "stem",
  "packPrice": 89.25,
  "currency": "USD",
  "availability": "InStock",
  "inStock": true,
  "minimumOrderQuantity": 25,
  "orderMultiple": 25,
  "stemsPerBunch": 25,
  "flowersPerStem": "1",
  "seasonalAvailability": "January-December",
  "vaseLife": "5-7 days",
  "pricingTiers": [
    {"minQuantity":25,"maxQuantity":75,"unitPrice":3.57,"unit":"stem","quantityText":"25 to 75 stems"},
    {"minQuantity":100,"maxQuantity":null,"unitPrice":3.44,"unit":"stem","quantityText":"100 or more stems"}
  ]
}
```

### All-in Pay per event pricing

One synthetic start event is charged per run at the supported 256 MB allocation, including runs that produce no products. One synthetic dataset-item event is charged for each unique, validated product delivered. There are no separate enrichment events or additional platform-usage pass-through fees.

| Tier | Discount | Start per run | Per product |
| --- | ---: | ---: | ---: |
| FREE | 0% | $0.00625 | $0.002 |
| BRONZE | 2.5% | $0.00609375 | $0.00195 |
| SILVER | 5% | $0.00593750 | $0.00190 |
| GOLD | 20% | $0.00500 | $0.00160 |

At list prices, one product costs $0.00825, five products $0.01625, ten products $0.02625, and one hundred products $0.20625. A no-match, unavailable page, blocked request, or rejected input has zero product events, with the start fee still applying. The minimum maximum-total-charge setting is $0.01. Set a suitable budget for your intended result count; the Actor checks remaining chargeable events before crawling and before every write. A budget that can cover only some products returns those products and stops with `CHARGE_LIMIT`.

Verified free-plan runs deliver at most five products, even when requesting more. An absent local plan value does not trigger this cap. Paid runs remain bounded by `maxResults`, the charge budget, time, pages, and requests. This Actor has no external API or proxy cost dependency.

### Reliability, limits and outcomes

The Actor reads public HTML using an identifying user agent, validates redirects, and respects robots.txt for that agent. It uses product microdata, semantic page elements, and catalog JSON-LD as an extraction fallback. There is no authentication, checkout interaction, CAPTCHA bypass, or proprietary source API. A 12-second request timeout, two retries with backoff for transient network/server failures, a 120-attempt ceiling, a 4 MB response ceiling, and a maximum 240-second scraping window bound resource use. The default cloud timeout is 300 seconds to leave time for finalization.

`DATA` means products were delivered; `EMPTY` means a healthy catalog had no matching products within the visited scope. `REJECTED` reports invalid or unsupported input, `BLOCKED` reports access denial or rate limiting, `FAILED` reports extraction/network failures, and `PARTIAL` preserves valid products when some requested work failed. `STOPPED` reports a resource or charge cap before delivery. A missing page is reported in warnings and is not presented as a healthy empty catalog. A successful container alone does not establish successful product extraction: always inspect `OUTPUT`.

Product IDs are deduplicated before writing, including across overlapping categories. A compact checkpoint and durable dataset IDs prevent duplicate delivery after migration/restart. Graceful lifecycle events preserve the checkpoint and summary; hard aborts may interrupt the current request. Existing datasets should not be reused for unrelated fresh inputs. The Actor stops on systemic errors instead of repeatedly spending against an unavailable site. Large keyword/stock-filtered catalogs can hit request or runtime bounds before finding the requested count; increase the scope carefully and inspect the stop reason.

### Appropriate use and support

Use this for public flower catalog research and sourcing snapshots. It is independent of and unaffiliated with Blooms by the Box. Respect the retailer's terms, image/content rights, and applicable rules for your use. It does not collect customer details, reviews, private account information, or place orders. Verify current price, availability, and shipping conditions directly with the retailer before purchasing.

For support, provide the Apify run ID, public source URL, expected field, and `OUTPUT` warning. Do not send credentials. Source code, schemas, and bounded validation evidence are maintained with the Actor; publication and branding remain owner-controlled.

# Actor input Schema

## `startUrls` (type: `array`):

Use public category or product URLs, such as https://www.bloomsbythebox.com/in-stock-roses/. Supply 1–10 URLs; duplicate products are delivered once. Account pages, checkout, internal endpoints and sitewide search are unsupported.

## `maxResults` (type: `integer`):

Use to bound delivered products across all URLs. Default 10; range 1–100. Verified free-plan runs are capped at 5 products. The maximum total charge can impose a smaller limit.

## `keyword` (type: `string`):

Use to filter product names within your supplied categories, for example White. Case-insensitive Unicode text, at most 100 characters; empty includes every name. This is a local catalog filter, not sitewide search.

## `inStockOnly` (type: `boolean`):

Use true to return only products with explicit InStock availability. Default false also permits PreOrder, OutOfStock and unknown availability. Seasonal availability is a separate field.

## `maxPages` (type: `integer`):

Use to bound category pages across all start URLs and public next-page links. Default 5; range 1–10. Most category pages expose their complete category in one HTML response. Product detail requests do not count as catalog pages.

## `maxRuntimeSecs` (type: `integer`):

Use to bound scraping time before finalization. Default 180 seconds; range 15–240. Requests have a 12-second timeout, two retries and a 120-attempt cap. Valid partial data is preserved.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.bloomsbythebox.com/in-stock-roses/"
    }
  ],
  "maxResults": 10,
  "keyword": "",
  "inStockOnly": false,
  "maxPages": 5,
  "maxRuntimeSecs": 180
}
```

# Actor output Schema

## `products` (type: `string`):

Validated product dataset; empty when no products were delivered.

## `summary` (type: `string`):

Outcome, delivered count, warnings, caps and stop reason.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://www.bloomsbythebox.com/in-stock-roses/"
        }
    ],
    "maxResults": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammadafzal/blooms-by-the-box-flower-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": [{ "url": "https://www.bloomsbythebox.com/in-stock-roses/" }],
    "maxResults": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("muhammadafzal/blooms-by-the-box-flower-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://www.bloomsbythebox.com/in-stock-roses/"
    }
  ],
  "maxResults": 10
}' |
apify call muhammadafzal/blooms-by-the-box-flower-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,muhammadafzal/blooms-by-the-box-flower-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/dhoVa5nWTtsaCYxtM/builds/muygnJjHoSjrptayL/openapi.json
