---
title: "Import a recipe from URL, video, text, or JSON-LD"
method: POST
path: "/api/v1/recipes/import"
tags: ["Recipes"]
---

# Import a recipe from URL, video, text, or JSON-LD

`POST /api/v1/recipes/import`

## Request

- Base URL: `https://spoonjoy.app`
- URL: `https://spoonjoy.app/api/v1/recipes/import`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `__session`
  - OAuth 2 (authorization code, authorization URL `https://spoonjoy.app/oauth/authorize`, token URL `https://spoonjoy.app/oauth/token`, scopes: `kitchen:write`)

## Headers

- `X-Request-Id` string

## Request body

- RecipeImportRequest
  - `clientMutationId` string, required
  - `source` union, required
    - object
      - `type` 'url', required
      - `url` string, uri, required
    - object
      - `type` 'video-url', required
      - `url` string, uri, required
    - object
      - `type` 'text', required
      - `text` string, required
      - `url` string, nullable
      - `capture` RecipeImportCapture
        - `source` 'camera' | 'photo-library', required
        - `assetIdentifier` string, nullable
    - object
      - `type` 'json-ld', required
      - `jsonLd` union, required
        - object
        - string
      - `url` string, nullable
  - `dryRun` boolean

## Response `200`

Success

- RecipeImportEnvelope
  - `ok` unknown, required
  - `requestId` string, required
  - `data` RecipeImportData, required
    - `recipe` RecipeDetail, required
      - `id` string, required
      - `title` string, required
      - `description` string, nullable, required
      - `servings` string, nullable, required
      - `chef` ChefSummary, required
        - `id` string, required
        - `username` string, required
        - `photoUrl` string, uri, nullable
      - `coverImageUrl` string, nullable, required: Public cover image URL for transient display. Spoonjoy-hosted /photos/ URLs accept ?w=<pixels> for a smaller WebP; see photoVariants in the API root. API v1 does not provide image alt text or a license to copy/store photos outside Spoonjoy.
      - `coverProvenanceLabel` string, nullable, required: Human-readable active cover provenance label such as Original photo, Editorial photo, Imported photo, or AI generated.
      - `coverSourceType` 'ai-placeholder' | 'chef-upload' | 'import' | 'spoon' | 'null', nullable, required: Active cover provenance source. Null when the recipe has no active public cover.
      - `coverVariant` 'image' | 'stylized' | 'null', nullable, required: Active cover variant: image is verbatim/pure AI/imported display; stylized is an editorialized chef photo. Null when no active cover is available.
      - `href` string, required
      - `canonicalUrl` string, uri, required
      - `attribution` RecipeAttribution, required
        - `creditText` string, required
        - `canonicalUrl` string, uri, required
        - `sourceUrl` string, nullable, required: User-provided provenance URL. Validate and allow-list schemes before rendering as a link.
        - `sourceHost` string, nullable, required
        - `sourceRecipe` SourceRecipeAttribution, required
          - `id` string, required
          - `title` string, nullable, required
          - `chef` ChefSummary, required
            - `id` string, required
            - `username` string, required
            - `photoUrl` string, uri, nullable
          - `href` string, nullable, required
          - `canonicalUrl` string, uri, nullable, required
          - `deleted` boolean, required
      - `createdAt` string, date-time, required
      - `updatedAt` string, date-time, required
      - `steps` RecipeStep[], required: Recipe steps returned in ascending stepNum order.
        - `id` string, required
        - `stepNum` integer, required: Display order. Recipe detail responses return steps in ascending stepNum order.
        - `stepTitle` string, nullable, required: Optional author-provided step heading. Render as text, not HTML.
        - `description` string, required: Author-provided instruction text. Render as text, not HTML.
        - `duration` integer, nullable, required: Minutes, when the author supplied a duration. Null means no duration was set.
        - `ingredients` RecipeIngredient[], required: Ingredients attached to this step, in API order. API v1 does not expose a separate ingredient display-text field.
          - `id` string, required
          - `name` string, required: Author-provided ingredient name. Render as text, not HTML.
          - `quantity` number, required: Author-provided numeric quantity. API v1 does not expose a separate free-form display line or conversion metadata.
          - `unit` string, nullable, required: Author-provided free-form display unit. Null when unset; not a canonical unit registry value.
        - `usingSteps` RecipeStepOutputUse[], required: Prior recipe steps whose outputs this step uses, ordered by outputStepNum.
          - `id` string, required
          - `inputStepNum` integer, required: Step number that consumes output from another step.
          - `outputStepNum` integer, required: Step number whose output is used by inputStepNum.
          - `outputOfStep` object, required
            - `stepNum` integer, required
            - `stepTitle` string, nullable, required
      - `cookbooks` CookbookLink[], required
        - `id` string, required
        - `title` string, required
        - `href` string, required
        - `canonicalUrl` string, uri, required
    - `importCode` string, nullable, required
    - `blockers` RecipeImportBlocker[], required
      - `capability` 'ProviderSecret', required
      - `provider` 'openai', required
      - `resource` 'recipe-import', required
      - `retryAfterSeconds` integer
    - `confidence` 'high' | 'medium' | 'low' | 'null', nullable
    - `source` 'json-ld' | 'llm' | 'mixed' | 'video-oembed-llm' | 'null', nullable
    - `existingRecipeId` string, nullable
    - `coverPending` boolean
    - `mutation` MutationMetadata, required
      - `clientMutationId` string, required
      - `replayed` boolean, required

## Other responses

- `201`: Success
- `400`: Errors: invalid_json, validation_error
- `401`: Errors: authentication_required, invalid_token
- `403`: Errors: insufficient_scope
- `405`: Errors: method_not_allowed
- `409`: Errors: idempotency_conflict, idempotency_in_progress
- `429`: Errors: rate_limited
- `500`: Errors: internal_error
- `502`: Errors: upstream_error
- `503`: Errors: temporarily_unavailable
- `504`: Errors: upstream_timeout

---

[API](https://skmtc.dev/spoonjoy/apis/spoonjoy-api.md) · [All operations](https://skmtc.dev/spoonjoy/apis/spoonjoy-api/llms.txt) · [OpenAPI document](https://skmtc.dev/spoonjoy/apis/spoonjoy-api/revisions/b96eb5d4697d?raw)
