{
  "openapi": "3.1.0",
  "info": {
    "title": "If The Card Fits — simulation API",
    "version": "2026-10-20.1",
    "summary": "Which credit cards are worth carrying for a household's spending.",
    "description": "Send a year of spending by category; get the three best cards and the best two-card combination, scored over five years against card terms verified weekly against the issuers' own pages.\n\nThis API answers questions about a household, not about a card. It will not value a single card you name, score a list you supply, return the full ranking, break earnings down by category, or hand back rates, caps or point values — each of those turns an answer into a way to read the model off its own output. Card terms are public on the card pages, and the point values are on /method/.\n\nIt does not accept statements, PDFs or transaction lists. Read those on the person's own device and send the totals.\n\nEvery answer carries a continueUrl that reopens the person's own numbers on the site; please show it to them, along with the disclosure string.",
    "contact": {
      "url": "https://ifthecardfits.com/api/"
    }
  },
  "servers": [
    {
      "url": "https://ifthecardfits.com"
    }
  ],
  "paths": {
    "/api/simulate": {
      "post": {
        "operationId": "simulate",
        "summary": "Best cards for a household's spending",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "totals",
                  "source"
                ],
                "properties": {
                  "totals": {
                    "type": "object",
                    "description": "A year's card spending in dollars by category. At least 4 categories must have spending, no single category may exceed 70% of the total, and the total must be between $3,000 and $500,000. Amounts are rounded to the nearest $50 before scoring.",
                    "additionalProperties": false,
                    "properties": {
                      "dining": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Dining & delivery"
                      },
                      "groceries": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Groceries"
                      },
                      "superstores": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Walmart & Target"
                      },
                      "wholesale_clubs": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Costco & warehouse clubs"
                      },
                      "gas": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Gas & EV charging"
                      },
                      "transit": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Transit, rideshare & parking"
                      },
                      "airfare": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Flights"
                      },
                      "hotels": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Hotels"
                      },
                      "car_rental": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Car rentals"
                      },
                      "travel_other": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Other travel"
                      },
                      "streaming": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Streaming"
                      },
                      "drugstores": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Drugstores"
                      },
                      "home_improvement": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Home improvement"
                      },
                      "entertainment": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Entertainment & events"
                      },
                      "amazon": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Amazon"
                      },
                      "online_retail": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Other online shopping"
                      },
                      "phone_internet": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Phone, internet & cable"
                      },
                      "fitness": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Gyms & fitness"
                      },
                      "other": {
                        "type": "number",
                        "minimum": 0,
                        "description": "Everything else"
                      }
                    }
                  },
                  "foreignPerYear": {
                    "type": "number",
                    "minimum": 0,
                    "description": "How much of that spending is in a foreign currency."
                  },
                  "memberships": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 8,
                    "description": "Membership tags they already pay for, e.g. amazon_prime."
                  },
                  "departures": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 200,
                    "description": "Flights departed per year; decides what lounge and companion perks are worth."
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "cash",
                      "travel"
                    ],
                    "default": "cash"
                  },
                  "years": {
                    "type": "integer",
                    "enum": [
                      1,
                      3,
                      5
                    ],
                    "default": 5
                  },
                  "held": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 5,
                    "description": "Card ids they already carry."
                  },
                  "filters": {
                    "type": "object",
                    "additionalProperties": false,
                    "properties": {
                      "noForeignFee": {
                        "type": "boolean"
                      },
                      "visaOrMastercard": {
                        "type": "boolean"
                      },
                      "feeCap": {
                        "type": "string",
                        "enum": [
                          "any",
                          "400",
                          "100",
                          "0"
                        ]
                      },
                      "creditTier": {
                        "type": "string",
                        "enum": [
                          "fair",
                          "good",
                          "excellent",
                          ""
                        ]
                      },
                      "homeAirport": {
                        "type": "string",
                        "pattern": "^([A-Z]{3})?$"
                      }
                    }
                  },
                  "source": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9-]{0,38}$",
                    "description": "A short slug naming your client. Required, so apply clicks can be attributed."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The recommendation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "catalogVersion",
                    "asOf",
                    "mode",
                    "years",
                    "best",
                    "bestPair",
                    "closeCall",
                    "unpriced",
                    "continueUrl",
                    "summary",
                    "disclosure",
                    "notAdvice",
                    "method"
                  ],
                  "properties": {
                    "catalogVersion": {
                      "type": "string",
                      "description": "Which version of the card catalog answered."
                    },
                    "asOf": {
                      "type": "string",
                      "format": "date"
                    },
                    "mode": {
                      "type": "string",
                      "enum": [
                        "cash",
                        "travel"
                      ]
                    },
                    "years": {
                      "type": "integer"
                    },
                    "best": {
                      "type": "array",
                      "maxItems": 3,
                      "items": {
                        "type": "object",
                        "required": [
                          "cardId",
                          "name",
                          "annualFee",
                          "valueOverYears",
                          "valuePerYear",
                          "why",
                          "termsCheckedOn",
                          "cardUrl"
                        ],
                        "properties": {
                          "cardId": {
                            "type": "string",
                            "example": "chase-sapphire-preferred"
                          },
                          "name": {
                            "type": "string"
                          },
                          "annualFee": {
                            "type": "integer"
                          },
                          "valueOverYears": {
                            "type": "integer",
                            "description": "What the card is worth over the horizon, after its fee. Rounded to $25."
                          },
                          "valuePerYear": {
                            "type": "integer",
                            "description": "The same figure per year. Rounded to $25."
                          },
                          "why": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "The issuer's own wording for the earn rules that matched this household's biggest categories, quoted verbatim."
                          },
                          "termsCheckedOn": {
                            "type": "string",
                            "description": "When a person last checked this card against the issuer's page."
                          },
                          "cardUrl": {
                            "type": "string",
                            "format": "uri",
                            "description": "The card's page on this site, carrying the source you sent."
                          },
                          "applyUrl": {
                            "type": "string",
                            "format": "uri",
                            "description": "A direct apply link. Usually absent: the apply click belongs on our page, where the affiliate placement and the disclosure are ours."
                          }
                        }
                      }
                    },
                    "bestPair": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "cardIds": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "minItems": 2,
                          "maxItems": 2
                        },
                        "valueOverYears": {
                          "type": "integer"
                        }
                      }
                    },
                    "closeCall": {
                      "type": "boolean",
                      "description": "True when the top two are within $50 a year: tell them it's a toss-up."
                    },
                    "unpriced": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Perks we refuse to put a number on."
                    },
                    "continueUrl": {
                      "type": "string",
                      "format": "uri",
                      "description": "Reopens their own numbers on the site. The spending rides in the fragment, so it never reaches a server."
                    },
                    "summary": {
                      "type": "string",
                      "description": "A sentence you can show as-is. It contains the continueUrl."
                    },
                    "disclosure": {
                      "type": "string",
                      "description": "Show this wherever you show the results."
                    },
                    "notAdvice": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body wasn't JSON."
          },
          "413": {
            "description": "The body was over 8 KB."
          },
          "422": {
            "description": "The request parsed but broke one of the rules above. The error names which one.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Retry-After says how long to wait."
          }
        }
      },
      "options": {
        "operationId": "simulatePreflight",
        "summary": "CORS preflight",
        "responses": {
          "204": {
            "description": "Allowed."
          }
        }
      }
    }
  },
  "externalDocs": {
    "url": "https://ifthecardfits.com/api/",
    "description": "Documentation, and the MCP server at /mcp"
  }
}