{
  "openapi": "3.1.0",
  "info": {
    "title": "IndieSheet Public API",
    "version": "1.0.0",
    "summary": "Read-only JSON API for the IndieSheet launch directory.",
    "description": "IndieSheet is a weekly launch platform and directory for independent software products. This API exposes the public directory: live products, a single product page, launch pricing and the DR Club leaderboard. It is read-only and needs no authentication or API key. Submitting, upvoting and editing require a signed-in person on indiesheet.com and are not part of this API. Responses are JSON; every public page is also available as Markdown with `Accept: text/markdown`.",
    "contact": {
      "name": "IndieSheet",
      "email": "venkatsolobuilder@gmail.com",
      "url": "https://indiesheet.com/contact"
    },
    "termsOfService": "https://indiesheet.com/terms",
    "license": {
      "name": "IndieSheet Terms of Service",
      "url": "https://indiesheet.com/terms"
    }
  },
  "externalDocs": {
    "description": "IndieSheet developer documentation",
    "url": "https://indiesheet.com/developers"
  },
  "servers": [
    {
      "url": "https://indiesheet.com",
      "description": "Production"
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "Products",
      "description": "Live products in the IndieSheet directory."
    },
    {
      "name": "Launches",
      "description": "How launching on IndieSheet is priced."
    },
    {
      "name": "DR Club",
      "description": "Leaderboard of maker websites by Ahrefs Domain Rating."
    }
  ],
  "paths": {
    "/api/products": {
      "get": {
        "operationId": "listProducts",
        "summary": "List live products",
        "description": "Returns every live product in the IndieSheet directory, ordered by total upvotes (highest first), then by name. Use it to discover indie tools or to find a product slug for getProduct.",
        "tags": [
          "Products"
        ],
        "responses": {
          "200": {
            "description": "All live products.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "products"
                  ],
                  "properties": {
                    "products": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ProductSummary"
                      }
                    },
                    "canUpvote": {
                      "type": "boolean",
                      "description": "Whether the current visitor may upvote. Always true for anonymous API clients, who still need to sign in on the website to vote."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Client error, such as an unknown route or an unsupported method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/products/{slug}": {
      "get": {
        "operationId": "getProduct",
        "summary": "Get one product",
        "description": "Returns the full public page of one live product: its listing fields, makers, related products, the editorial profile (overview, features, use cases) as a JSON string, and DR Club data when the site is tracked. The same page is human-readable at /tool/{slug}.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "The product slug, as used in https://indiesheet.com/tool/{slug}. Lowercase letters, digits and hyphens.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]+$",
              "examples": [
                "rotli"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The product.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "product"
                  ],
                  "properties": {
                    "product": {
                      "$ref": "#/components/schemas/Product"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No live product has this slug.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "4XX": {
            "description": "Client error, such as an unknown route or an unsupported method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/pricing": {
      "get": {
        "operationId": "getLaunchPricing",
        "summary": "Get launch pricing",
        "description": "Returns whether the paid instant launch is available and its price label. A launch with a verified IndieSheet badge is always free.",
        "tags": [
          "Launches"
        ],
        "responses": {
          "200": {
            "description": "Current launch pricing.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "instantLaunch"
                  ],
                  "properties": {
                    "instantLaunch": {
                      "type": "object",
                      "required": [
                        "enabled",
                        "price"
                      ],
                      "properties": {
                        "enabled": {
                          "type": "boolean",
                          "description": "Whether instant launches can be bought right now."
                        },
                        "price": {
                          "type": "string",
                          "description": "Display price, for example \"$5\"."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Client error, such as an unknown route or an unsupported method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/dr-club": {
      "get": {
        "operationId": "getDrClubLeaderboard",
        "summary": "Get the DR Club leaderboard",
        "description": "Returns maker websites tracked in the IndieSheet DR Club, with their current Ahrefs Domain Rating, 30- and 90-day changes and club tier, plus leaderboard summaries.",
        "tags": [
          "DR Club"
        ],
        "responses": {
          "200": {
            "description": "The DR Club leaderboard.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "entries"
                  ],
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DrClubEntry"
                      }
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "4XX": {
            "description": "Client error, such as an unknown route or an unsupported method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ProductSummary": {
        "type": "object",
        "description": "A live product as listed in the directory.",
        "required": [
          "slug",
          "name",
          "description",
          "category",
          "type",
          "price",
          "maker",
          "websiteUrl",
          "upvotes"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Stable identifier; the page is /tool/{slug}."
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "description": "One-line description, at most 280 characters."
          },
          "category": {
            "type": "string",
            "description": "Directory category, for example \"Productivity\"."
          },
          "type": {
            "type": "string",
            "description": "Product type, for example \"SaaS\" or \"App\"."
          },
          "price": {
            "type": "string",
            "description": "Pricing model: \"Free\", \"Freemium\", \"Paid\", \"Contact for pricing\", or empty when unknown."
          },
          "maker": {
            "type": "string",
            "description": "Maker or team name."
          },
          "launched": {
            "type": [
              "string",
              "null"
            ],
            "description": "Year the product launched, when known."
          },
          "websiteUrl": {
            "type": "string",
            "format": "uri",
            "description": "The product's own website."
          },
          "logoUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Logo path on indiesheet.com, or empty."
          },
          "accent": {
            "type": "string",
            "description": "Logo tile colour used when there is no logo."
          },
          "initials": {
            "type": "string",
            "description": "Logo tile initials used when there is no logo."
          },
          "upvotes": {
            "type": "integer",
            "minimum": 0,
            "description": "Total upvotes."
          },
          "upvoted": {
            "type": "integer",
            "enum": [
              0,
              1
            ],
            "description": "Whether the signed-in visitor upvoted it; always 0 for anonymous clients."
          },
          "linkNofollow": {
            "type": "integer",
            "enum": [
              0,
              1
            ],
            "description": "1 when the website link is nofollow."
          },
          "domainRating": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Last known Ahrefs Domain Rating of the website."
          }
        }
      },
      "Product": {
        "description": "A product with its full public page.",
        "allOf": [
          {
            "$ref": "#/components/schemas/ProductSummary"
          },
          {
            "type": "object",
            "properties": {
              "publishedAt": {
                "type": "string",
                "description": "When the product went live (UTC, \"YYYY-MM-DD HH:MM:SS\")."
              },
              "profileJson": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "JSON-encoded editorial profile with shortDescription, about, bestFor, features ([title, text] pairs), useCases, screenshots, logoUrl and sources."
              },
              "makers": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "handle": {
                      "type": "string",
                      "description": "Maker page is /makers/{handle}."
                    },
                    "displayName": {
                      "type": "string"
                    },
                    "avatarUrl": {
                      "type": "string"
                    }
                  }
                }
              },
              "moreByMaker": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/RelatedProduct"
                }
              },
              "related": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/RelatedProduct"
                }
              },
              "relatedBasis": {
                "type": "string",
                "enum": [
                  "category",
                  "type"
                ],
                "description": "Whether related products share the category or, for thin categories, the type."
              },
              "dr": {
                "$ref": "#/components/schemas/DrClubEntry",
                "description": "Present when the product's website is tracked in the DR Club."
              }
            }
          }
        ]
      },
      "RelatedProduct": {
        "type": "object",
        "required": [
          "slug",
          "name"
        ],
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "price": {
            "type": "string"
          },
          "initials": {
            "type": "string"
          },
          "accent": {
            "type": "string"
          },
          "logoUrl": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "DrClubEntry": {
        "type": "object",
        "description": "A website tracked in the DR Club.",
        "required": [
          "name",
          "domain",
          "websiteUrl"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "domain": {
            "type": "string"
          },
          "websiteUrl": {
            "type": "string",
            "format": "uri"
          },
          "slug": {
            "type": "string",
            "description": "IndieSheet product slug when the site is a listed product, otherwise empty."
          },
          "logoUrl": {
            "type": "string"
          },
          "founderName": {
            "type": "string"
          },
          "founderXUrl": {
            "type": "string"
          },
          "currentDr": {
            "type": [
              "number",
              "null"
            ],
            "description": "Current Ahrefs Domain Rating (0-100)."
          },
          "change30": {
            "type": [
              "number",
              "null"
            ],
            "description": "DR change over 30 days."
          },
          "change90": {
            "type": [
              "number",
              "null"
            ],
            "description": "DR change over 90 days."
          },
          "currentTier": {
            "type": [
              "number",
              "null"
            ],
            "description": "Highest DR club tier reached, for example 10 or 20."
          },
          "currentClubLabel": {
            "type": "string"
          },
          "lastCheckedAt": {
            "type": [
              "string",
              "null"
            ],
            "description": "When DR was last refreshed."
          },
          "trackingEnabled": {
            "type": "boolean"
          }
        },
        "additionalProperties": true
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message."
          }
        }
      }
    }
  }
}