{
  "openapi": "3.1.0",
  "info": {
    "title": "Profit-First Amazon PPC tools API",
    "version": "1.1.0",
    "description": "Free, no-key JSON APIs for the calculators at https://profitfirstppc.com/tools/. Each endpoint runs the same code as its web page. Every calculation is built on profit per order before ad spend, not ACoS targets. GET an endpoint with no parameters to see its usage. Limit: about 30 requests a minute per address. For bulk checks, use the batch POST (up to 500 rows per request).",
    "contact": {
      "name": "Radu Bobirnac",
      "url": "https://profitfirstppc.com/about.html"
    }
  },
  "servers": [
    {
      "url": "https://profitfirstppc.com"
    }
  ],
  "externalDocs": {
    "description": "Tool pages with the method, worked examples and limits",
    "url": "https://profitfirstppc.com/tools/"
  },
  "paths": {
    "/api/harvest-readiness": {
      "get": {
        "operationId": "checkHarvestReadiness",
        "summary": "Is a search term ready to harvest into exact match?",
        "description": "Beta-binomial shrinkage of the term's conversion rate toward its campaign's, then the probability its true rate beats break-even (CPC \u00f7 profit per order). Docs: https://profitfirstppc.com/tools/harvest-readiness/",
        "parameters": [
          {
            "name": "clicks",
            "in": "query",
            "required": true,
            "description": "Clicks on the search term.",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "orders",
            "in": "query",
            "required": true,
            "description": "Attributed orders (7 Day Total Orders in the Search Term Report).",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "spend",
            "in": "query",
            "required": false,
            "description": "Total ad spend on the term, in dollars. Give spend or cpc.",
            "schema": {
              "type": "number",
              "minimum": 0
            }
          },
          {
            "name": "cpc",
            "in": "query",
            "required": false,
            "description": "Average cost per click. Used instead of spend if given.",
            "schema": {
              "type": "number",
              "exclusiveMinimum": 0
            }
          },
          {
            "name": "profit_per_order",
            "in": "query",
            "required": true,
            "description": "Profit per order before ad spend: price minus Amazon fees and landed cost.",
            "schema": {
              "type": "number",
              "exclusiveMinimum": 0
            }
          },
          {
            "name": "baseline_cvr",
            "in": "query",
            "required": false,
            "description": "Conversion rate of the campaign the term came from, as a fraction (0.10) or percent (10). Default 0.10.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "prior_clicks",
            "in": "query",
            "required": false,
            "description": "How many clicks of evidence the baseline is worth (skepticism). Default 30.",
            "schema": {
              "type": "number",
              "exclusiveMinimum": 0,
              "maximum": 10000
            }
          },
          {
            "name": "confidence",
            "in": "query",
            "required": false,
            "description": "Chance of profit needed to harvest, 0.5\u20130.99 (or 50\u201399). Default 0.90.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "price",
            "in": "query",
            "required": false,
            "description": "Optional selling price; adds breakEvenAcos to the result.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "search_term",
            "in": "query",
            "required": false,
            "description": "Optional label, echoed back.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verdict",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tool": {
                      "type": "string"
                    },
                    "methodVersion": {
                      "type": "string"
                    },
                    "docs": {
                      "type": "string",
                      "format": "uri"
                    },
                    "openapi": {
                      "type": "string",
                      "format": "uri"
                    },
                    "result": {
                      "$ref": "#/components/schemas/HarvestResult"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input. Each error names the field and says what to send.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from one address. Wait a minute."
          }
        }
      },
      "post": {
        "operationId": "checkHarvestReadinessBatch",
        "summary": "Check one search term, or up to 500 from a Search Term Report",
        "description": "Send one term's fields, or { defaults, terms: [...] }. Defaults apply to every term unless the term overrides them.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/HarvestInput"
                  },
                  {
                    "type": "object",
                    "required": [
                      "terms"
                    ],
                    "properties": {
                      "defaults": {
                        "$ref": "#/components/schemas/HarvestInput"
                      },
                      "terms": {
                        "type": "array",
                        "maxItems": 500,
                        "items": {
                          "$ref": "#/components/schemas/HarvestInput"
                        }
                      }
                    }
                  }
                ]
              },
              "example": {
                "defaults": {
                  "profitPerOrder": 12,
                  "baselineCvr": 0.1
                },
                "terms": [
                  {
                    "searchTerm": "insulated water bottle",
                    "clicks": 60,
                    "orders": 12,
                    "spend": 72
                  },
                  {
                    "searchTerm": "water bottle with straw",
                    "clicks": 9,
                    "orders": 1,
                    "spend": 10.8
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "One result, or summary + results for a batch",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "tool": {
                          "type": "string"
                        },
                        "methodVersion": {
                          "type": "string"
                        },
                        "docs": {
                          "type": "string",
                          "format": "uri"
                        },
                        "openapi": {
                          "type": "string",
                          "format": "uri"
                        },
                        "result": {
                          "$ref": "#/components/schemas/HarvestResult"
                        }
                      }
                    },
                    {
                      "type": "object",
                      "properties": {
                        "tool": {
                          "type": "string"
                        },
                        "methodVersion": {
                          "type": "string"
                        },
                        "docs": {
                          "type": "string",
                          "format": "uri"
                        },
                        "openapi": {
                          "type": "string",
                          "format": "uri"
                        },
                        "summary": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          },
                          "description": "Count of results per verdict (\"error\" for rows with invalid input)."
                        },
                        "results": {
                          "type": "array",
                          "items": {
                            "allOf": [
                              {
                                "$ref": "#/components/schemas/HarvestResult"
                              },
                              {
                                "type": "object",
                                "properties": {
                                  "index": {
                                    "type": "integer"
                                  },
                                  "errors": {
                                    "type": "array",
                                    "items": {
                                      "type": "object"
                                    }
                                  }
                                }
                              }
                            ]
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "413": {
            "description": "More than 500 terms"
          },
          "400": {
            "description": "Invalid input. Each error names the field and says what to send.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from one address. Wait a minute."
          }
        }
      }
    },
    "/api/when-to-negate": {
      "get": {
        "operationId": "checkWhenToNegate",
        "summary": "Is a search term converting below break-even? Should it be negated?",
        "description": "Exact binomial test of the term's orders from its clicks against the break-even conversion rate (CPC \u00f7 profit per sale). Docs: https://profitfirstppc.com/tools/when-to-negate/",
        "parameters": [
          {
            "name": "clicks",
            "in": "query",
            "required": true,
            "description": "Clicks on the search term.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "orders",
            "in": "query",
            "required": true,
            "description": "Attributed orders (0 if none).",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "spend",
            "in": "query",
            "required": true,
            "description": "Total ad spend on the term, in dollars.",
            "schema": {
              "type": "number",
              "exclusiveMinimum": 0
            }
          },
          {
            "name": "profit_per_order",
            "in": "query",
            "required": false,
            "description": "Profit per sale before ads. Or give price and break_even_acos instead.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "price",
            "in": "query",
            "required": false,
            "description": "Selling price, used with break_even_acos.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "break_even_acos",
            "in": "query",
            "required": false,
            "description": "Break-even ACoS as a fraction (0.35) or percent (35), used with price.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "confidence",
            "in": "query",
            "required": false,
            "description": "0.8, 0.9 or 0.95 (default 0.95).",
            "schema": {
              "type": "number",
              "enum": [
                0.8,
                0.9,
                0.95
              ]
            }
          },
          {
            "name": "search_term",
            "in": "query",
            "required": false,
            "description": "Optional label, echoed back.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verdict",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tool": {
                      "type": "string"
                    },
                    "methodVersion": {
                      "type": "string"
                    },
                    "docs": {
                      "type": "string",
                      "format": "uri"
                    },
                    "openapi": {
                      "type": "string",
                      "format": "uri"
                    },
                    "result": {
                      "$ref": "#/components/schemas/NegateResult"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input. Each error names the field and says what to send.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from one address. Wait a minute."
          }
        }
      },
      "post": {
        "operationId": "checkWhenToNegateBatch",
        "summary": "Check one search term, or up to 500",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/NegateInput"
                  },
                  {
                    "type": "object",
                    "required": [
                      "terms"
                    ],
                    "properties": {
                      "defaults": {
                        "$ref": "#/components/schemas/NegateInput"
                      },
                      "terms": {
                        "type": "array",
                        "maxItems": 500,
                        "items": {
                          "$ref": "#/components/schemas/NegateInput"
                        }
                      }
                    }
                  }
                ]
              },
              "example": {
                "defaults": {
                  "profitPerOrder": 19.29
                },
                "terms": [
                  {
                    "searchTerm": "insulated water bottle",
                    "clicks": 24,
                    "orders": 0,
                    "spend": 28.8
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "One result, or summary + results for a batch",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "tool": {
                          "type": "string"
                        },
                        "methodVersion": {
                          "type": "string"
                        },
                        "docs": {
                          "type": "string",
                          "format": "uri"
                        },
                        "openapi": {
                          "type": "string",
                          "format": "uri"
                        },
                        "result": {
                          "$ref": "#/components/schemas/NegateResult"
                        }
                      }
                    },
                    {
                      "type": "object",
                      "properties": {
                        "tool": {
                          "type": "string"
                        },
                        "methodVersion": {
                          "type": "string"
                        },
                        "docs": {
                          "type": "string",
                          "format": "uri"
                        },
                        "openapi": {
                          "type": "string",
                          "format": "uri"
                        },
                        "summary": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          },
                          "description": "Count of results per verdict (\"error\" for rows with invalid input)."
                        },
                        "results": {
                          "type": "array",
                          "items": {
                            "allOf": [
                              {
                                "$ref": "#/components/schemas/NegateResult"
                              },
                              {
                                "type": "object",
                                "properties": {
                                  "index": {
                                    "type": "integer"
                                  },
                                  "errors": {
                                    "type": "array",
                                    "items": {
                                      "type": "object"
                                    }
                                  }
                                }
                              }
                            ]
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "413": {
            "description": "More than 500 terms"
          },
          "400": {
            "description": "Invalid input. Each error names the field and says what to send.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from one address. Wait a minute."
          }
        }
      }
    },
    "/api/placement-bids": {
      "post": {
        "operationId": "calculatePlacementBids",
        "summary": "Keyword bid and placement adjustments that keep top of search, rest of search and product pages under target ACoS",
        "description": "A click in each placement is worth (sales \u00f7 clicks) \u00d7 target ACoS. Returns two options: rebalance (the weakest placement sets the keyword bid) and keep today's keyword bid. Placements under 30 clicks don't set bids. Docs: https://profitfirstppc.com/tools/placement-bid-calculator/",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlacementInput"
              },
              "example": {
                "breakEvenAcos": 0.482,
                "marginToKeep": 0.1,
                "keywordBid": 1.0,
                "strategy": "fixed",
                "placements": {
                  "topOfSearch": {
                    "adjustment": 50,
                    "clicks": 400,
                    "spend": 560,
                    "orders": 60,
                    "sales": 2400
                  },
                  "restOfSearch": {
                    "adjustment": 0,
                    "clicks": 300,
                    "spend": 300,
                    "orders": 24,
                    "sales": 960
                  },
                  "productPages": {
                    "adjustment": 0,
                    "clicks": 250,
                    "spend": 200,
                    "orders": 8,
                    "sales": 320
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Per-placement values and the two bid options",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tool": {
                      "type": "string"
                    },
                    "methodVersion": {
                      "type": "string"
                    },
                    "docs": {
                      "type": "string",
                      "format": "uri"
                    },
                    "openapi": {
                      "type": "string",
                      "format": "uri"
                    },
                    "result": {
                      "$ref": "#/components/schemas/PlacementResult"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input. Each error names the field and says what to send.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from one address. Wait a minute."
          }
        }
      }
    },
    "/api/catalog-profit-check": {
      "get": {
        "operationId": "checkProductBreakEven",
        "summary": "Break-even ACoS, target ACoS and max CPC for one product",
        "description": "The one-product break-even calculator. Docs: https://profitfirstppc.com/tools/catalog-profit-check/",
        "parameters": [
          {
            "name": "price",
            "in": "query",
            "required": true,
            "description": "Selling price.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "fees",
            "in": "query",
            "required": true,
            "description": "Amazon referral + FBA fees per unit.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "cost",
            "in": "query",
            "required": true,
            "description": "Landed cost per unit.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "conversion_rate",
            "in": "query",
            "required": false,
            "description": "Optional ad conversion rate, fraction.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "acos",
            "in": "query",
            "required": false,
            "description": "Optional ACoS today, fraction.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "ad_sales",
            "in": "query",
            "required": false,
            "description": "Optional ad sales per month.",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "margin_to_keep",
            "in": "query",
            "required": false,
            "description": "Fraction of price to keep after ads. Default 0.10.",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tool": {
                      "type": "string"
                    },
                    "methodVersion": {
                      "type": "string"
                    },
                    "docs": {
                      "type": "string",
                      "format": "uri"
                    },
                    "openapi": {
                      "type": "string",
                      "format": "uri"
                    },
                    "result": {
                      "$ref": "#/components/schemas/CatalogResult"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input. Each error names the field and says what to send.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from one address. Wait a minute."
          }
        }
      },
      "post": {
        "operationId": "checkCatalogProfit",
        "summary": "Which products lose money on ads? Up to 500 products",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CatalogInput"
              },
              "example": {
                "marginToKeep": 0.1,
                "products": [
                  {
                    "name": "Water bottle 750ml",
                    "price": 39.99,
                    "fees": 11.2,
                    "cost": 9.5,
                    "conversionRate": 0.12,
                    "acos": 0.22,
                    "adSales": 18400
                  },
                  {
                    "name": "Bottle 2-pack",
                    "price": 24.99,
                    "fees": 9.1,
                    "cost": 11.4,
                    "conversionRate": 0.1,
                    "acos": 0.31,
                    "adSales": 9200
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Per-product results and catalog summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tool": {
                      "type": "string"
                    },
                    "methodVersion": {
                      "type": "string"
                    },
                    "docs": {
                      "type": "string",
                      "format": "uri"
                    },
                    "openapi": {
                      "type": "string",
                      "format": "uri"
                    },
                    "result": {
                      "$ref": "#/components/schemas/CatalogResult"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "More than 500 products"
          },
          "400": {
            "description": "Invalid input. Each error names the field and says what to send.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from one address. Wait a minute."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "HarvestInput": {
        "type": "object",
        "description": "camelCase or snake_case keys are both accepted.",
        "properties": {
          "searchTerm": {
            "type": "string"
          },
          "clicks": {
            "type": "integer",
            "minimum": 0
          },
          "orders": {
            "type": "integer",
            "minimum": 0
          },
          "spend": {
            "type": "number",
            "description": "Positive spend with positive clicks. If cpc is also supplied, it must agree with spend / clicks within half a cent."
          },
          "cpc": {
            "type": "number"
          },
          "profitPerOrder": {
            "type": "number"
          },
          "baselineCvr": {
            "type": "number"
          },
          "priorClicks": {
            "type": "number"
          },
          "confidence": {
            "type": "number",
            "description": "Model probability threshold, at least 0.5 and below 1. Not empirically calibrated."
          },
          "price": {
            "type": "number"
          }
        }
      },
      "HarvestResult": {
        "type": "object",
        "properties": {
          "searchTerm": {
            "type": [
              "string",
              "null"
            ]
          },
          "verdict": {
            "type": "string",
            "enum": [
              "harvest_now",
              "wait",
              "do_not_harvest",
              "no_evidence"
            ]
          },
          "headline": {
            "type": "string",
            "description": "One-sentence plain-English answer."
          },
          "probabilityProfitable": {
            "type": "number",
            "description": "P(true conversion rate > break-even conversion rate) under the beta posterior."
          },
          "observedCvr": {
            "type": [
              "number",
              "null"
            ]
          },
          "estimatedCvr": {
            "type": "number",
            "description": "Posterior mean conversion rate under the uncalibrated baseline weighting."
          },
          "breakEvenCvr": {
            "type": "number",
            "description": "CPC \u00f7 profit per order."
          },
          "luckShare": {
            "type": "number",
            "description": "Deprecated name for the assumed baseline weight, not a measured share of luck. Use priorWeight.",
            "deprecated": true
          },
          "cpc": {
            "type": "number"
          },
          "spend": {
            "type": "number"
          },
          "profitSoFar": {
            "type": "number"
          },
          "expectedProfitPer100Clicks": {
            "type": "number"
          },
          "maxProfitableBid": {
            "type": "number",
            "description": "Deprecated name for estimated break-even CPC, rounded to cents. Not an adjusted Amazon bid cap. Use breakEvenCpcAtEstimate.",
            "deprecated": true
          },
          "suggestedStartingBid": {
            "type": [
              "number",
              "null"
            ],
            "description": "Review-only base bid for fixed bidding with no placement or other modifiers. Null if below the minimum economic click value; does not guarantee CPC."
          },
          "extraClicksNeeded": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Conditional scenario adding fractional expected orders at the current posterior mean with unchanged CPC. Not a forecast or guaranteed stopping time. Null when the threshold is not reached in 2,000 extra clicks."
          },
          "breakEvenAcos": {
            "type": [
              "number",
              "null"
            ]
          },
          "explanation": {
            "type": "string"
          },
          "assumptions": {
            "type": "object"
          },
          "methodVersion": {
            "type": "string"
          },
          "priorWeight": {
            "type": "number",
            "description": "priorClicks / (priorClicks + clicks): assumed shrinkage weight."
          },
          "breakEvenCpcAtEstimate": {
            "type": "number",
            "description": "Posterior mean conversion rate times contribution per purchase, before bid adjustments."
          },
          "minimumBidExceedsWorth": {
            "type": "boolean",
            "description": "True if estimated click value is below the $0.02 minimum base bid. No starting bid is suggested."
          }
        }
      },
      "NegateInput": {
        "type": "object",
        "description": "camelCase or snake_case keys are both accepted.",
        "properties": {
          "searchTerm": {
            "type": "string"
          },
          "clicks": {
            "type": "integer"
          },
          "orders": {
            "type": "integer"
          },
          "spend": {
            "type": "number"
          },
          "profitPerOrder": {
            "type": "number"
          },
          "price": {
            "type": "number"
          },
          "breakEvenAcos": {
            "type": "number"
          },
          "confidence": {
            "type": "number",
            "enum": [
              0.8,
              0.9,
              0.95
            ]
          }
        }
      },
      "NegateResult": {
        "type": "object",
        "properties": {
          "searchTerm": {
            "type": [
              "string",
              "null"
            ]
          },
          "verdict": {
            "type": "string",
            "enum": [
              "negate",
              "watch",
              "early",
              "profitable",
              "no_profit_before_ads",
              "cpc_above_profit"
            ]
          },
          "headline": {
            "type": "string"
          },
          "probabilityIfBreakEven": {
            "type": [
              "number",
              "null"
            ],
            "description": "P(X \u2264 orders) for X ~ Binomial(clicks, break-even CVR): how often a term that exactly paid for its clicks would look this bad."
          },
          "breakEvenCvr": {
            "type": [
              "number",
              "null"
            ]
          },
          "observedCvr": {
            "type": "number"
          },
          "cpc": {
            "type": "number"
          },
          "profitPerOrder": {
            "type": "number"
          },
          "maxProfitableCpcAtObservedCvr": {
            "type": [
              "number",
              "null"
            ]
          },
          "clicksToBeSure": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Click count (or additional clicks) reaching the single-test tail-probability threshold with no additional orders and unchanged CPC. Not a posterior certainty or a historical stopping point."
          },
          "moreClicksToBeSure": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Click count (or additional clicks) reaching the single-test tail-probability threshold with no additional orders and unchanged CPC. Not a posterior certainty or a historical stopping point."
          },
          "spendToBeSure": {
            "type": [
              "number",
              "null"
            ]
          },
          "confidence": {
            "type": "number"
          },
          "notes": {
            "type": "string"
          },
          "methodVersion": {
            "type": "string"
          }
        }
      },
      "PlacementInput": {
        "type": "object",
        "required": [
          "breakEvenAcos",
          "keywordBid",
          "placements"
        ],
        "properties": {
          "breakEvenAcos": {
            "type": "number",
            "description": "Fraction (0.482) or percent (48.2)."
          },
          "marginToKeep": {
            "type": "number",
            "description": "Points of margin to keep after ads; fraction (0.10) or percent (10). Default 0.10. Target ACoS = breakEvenAcos \u2212 marginToKeep."
          },
          "keywordBid": {
            "type": "number",
            "description": "Typical keyword bid in the campaign, in dollars."
          },
          "strategy": {
            "type": "string",
            "enum": [
              "fixed",
              "down",
              "updown"
            ],
            "default": "fixed",
            "description": "Campaign bidding strategy: fixed bids, dynamic down only, or dynamic up and down."
          },
          "placements": {
            "type": "object",
            "properties": {
              "topOfSearch": {
                "type": "object",
                "properties": {
                  "adjustment": {
                    "type": "number",
                    "description": "Placement adjustment today, in percent as shown in the console (50 = +50%)."
                  },
                  "clicks": {
                    "type": "number"
                  },
                  "spend": {
                    "type": "number"
                  },
                  "orders": {
                    "type": "number"
                  },
                  "sales": {
                    "type": "number"
                  }
                }
              },
              "restOfSearch": {
                "type": "object",
                "properties": {
                  "adjustment": {
                    "type": "number",
                    "description": "Placement adjustment today, in percent as shown in the console (50 = +50%)."
                  },
                  "clicks": {
                    "type": "number"
                  },
                  "spend": {
                    "type": "number"
                  },
                  "orders": {
                    "type": "number"
                  },
                  "sales": {
                    "type": "number"
                  }
                }
              },
              "productPages": {
                "type": "object",
                "properties": {
                  "adjustment": {
                    "type": "number",
                    "description": "Placement adjustment today, in percent as shown in the console (50 = +50%)."
                  },
                  "clicks": {
                    "type": "number"
                  },
                  "spend": {
                    "type": "number"
                  },
                  "orders": {
                    "type": "number"
                  },
                  "sales": {
                    "type": "number"
                  }
                }
              }
            }
          }
        }
      },
      "PlacementResult": {
        "type": "object",
        "properties": {
          "targetAcos": {
            "type": "number"
          },
          "breakEvenAcos": {
            "type": "number"
          },
          "strategy": {
            "type": "string"
          },
          "placements": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "placement": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "no_data",
                    "too_few_clicks",
                    "losing_money",
                    "over_target",
                    "under_target"
                  ]
                },
                "clicks": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "cpc": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "conversionRate": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "acos": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "salesPerClick": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "clickWorth": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Sales per click \u00d7 target ACoS: the most a click can cost and stay on target."
                },
                "mostYouPayToday": {
                  "type": "number",
                  "description": "Keyword bid \u00d7 (1 + adjustment), \u00d7 dynamic uplift for updown."
                },
                "bidAboveWorth": {
                  "type": [
                    "boolean",
                    "null"
                  ]
                },
                "adProfitLost": {
                  "type": "number"
                }
              }
            }
          },
          "rebalance": {
            "type": "object",
            "properties": {
              "description": {
                "type": "string"
              },
              "keywordBid": {
                "type": "number"
              },
              "keywordBidChange": {
                "type": "number"
              },
              "adjustments": {
                "type": "object",
                "properties": {
                  "topOfSearch": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "restOfSearch": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "productPages": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  }
                },
                "description": "Placement adjustments in percent. null = too few clicks (under 30) to set."
              },
              "cappedPlacements": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "minimumBidHit": {
                "type": "boolean"
              }
            }
          },
          "keepKeywordBid": {
            "type": "object",
            "properties": {
              "description": {
                "type": "string"
              },
              "keywordBid": {
                "type": "number"
              },
              "adjustments": {
                "type": "object",
                "properties": {
                  "topOfSearch": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "restOfSearch": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "productPages": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  }
                },
                "description": "Placement adjustments in percent. null = too few clicks (under 30) to set."
              },
              "stillAboveWorth": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "weakestPlacement": {
            "type": "string"
          },
          "strongestPlacement": {
            "type": "string"
          },
          "usablePlacements": {
            "type": "integer"
          },
          "methodVersion": {
            "type": "string"
          }
        }
      },
      "CatalogInput": {
        "type": "object",
        "properties": {
          "marginToKeep": {
            "type": "number",
            "description": "Fraction, default 0.10."
          },
          "products": {
            "type": "array",
            "maxItems": 500,
            "items": {
              "type": "object",
              "required": [
                "price",
                "fees",
                "cost"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "price": {
                  "type": "number"
                },
                "fees": {
                  "type": "number",
                  "description": "Amazon referral + FBA fees per unit."
                },
                "cost": {
                  "type": "number",
                  "description": "Landed cost per unit."
                },
                "conversionRate": {
                  "type": "number",
                  "description": "Optional ad conversion rate, fraction (0.12). Adds maxCpc."
                },
                "acos": {
                  "type": "number",
                  "description": "Optional ACoS today, fraction (0.22)."
                },
                "adSales": {
                  "type": "number",
                  "description": "Optional ad sales per month, dollars. With acos, adds adProfitPerMonth."
                }
              }
            }
          }
        },
        "description": "Either { marginToKeep, products: [...] } or one product's fields at the top level."
      },
      "CatalogResult": {
        "type": "object",
        "properties": {
          "marginToKeep": {
            "type": "number"
          },
          "products": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer"
                },
                "name": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "no_room_for_ads",
                    "losing_money",
                    "over_target",
                    "room_to_scale",
                    "thin_margin",
                    "workable",
                    "healthy_margin"
                  ]
                },
                "profitBeforeAds": {
                  "type": "number"
                },
                "breakEvenAcos": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Profit before ads \u00f7 price."
                },
                "targetAcos": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "maxCpc": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Price \u00d7 target ACoS \u00d7 conversion rate."
                },
                "acosToday": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "adProfitPerMonth": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Ad sales \u00d7 (break-even ACoS \u2212 ACoS today)."
                },
                "errors": {
                  "type": "array",
                  "items": {
                    "type": "object"
                  }
                }
              }
            }
          },
          "summary": {
            "type": "object",
            "properties": {
              "productsChecked": {
                "type": "integer"
              },
              "breakEvenRange": {
                "type": [
                  "array",
                  "null"
                ],
                "items": {
                  "type": "number"
                }
              },
              "noRoomForAds": {
                "type": "integer"
              },
              "losingMoneyOnAds": {
                "type": "integer"
              },
              "adProfitPerMonth": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "monthlyLossOnLosingProducts": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "blendedAcos": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "blendedBreakEvenAcos": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "blendedAcosHidesLosers": {
                "type": "boolean",
                "description": "True when the sales-weighted ACoS is under the sales-weighted break-even while some products lose money."
              }
            }
          },
          "methodVersion": {
            "type": "string"
          }
        }
      }
    }
  }
}
