{
  "openapi": "3.1.1",
  "info": {
    "title": "Running Pace & Splits API",
    "version": "1.0.0",
    "summary": "Pace, time or distance in km or miles, splits, race predictions, treadmill speed, laps.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://runningpacecalculator.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Pace, time or distance in km or miles, splits, race predictions, treadmill speed, laps.",
        "description": "Enter two of distance, time and pace to get the third, in kilometres or miles. Get an even, negative or positive split table, race time predictions with the Riegel formula, the treadmill speed in km/h and mph, and track lap times.\n\nMethodology:\n- Pace = time ÷ distance; time = pace × distance; distance = time ÷ pace. 1 mile = 1609.344 m exactly; a half marathon is 21.0975 km and a marathon 42.195 km.\n- Speed (treadmill) = 3600 ÷ pace in seconds per km (km/h) or per mile (mph).\n- Splits: for an even pace every split takes pace × split length. For a negative (positive) split the pace changes linearly along the course by the chosen percentage from start to finish, so the split times still add up to the target time.\n- Race predictions use Riegel's formula T2 = T1 × (D2 ÷ D1)^1.06, which he described for efforts of roughly 3.5 to 230 minutes. Predictions far from the input distance, and marathon times for runners without long-run training, are usually too optimistic.\n- Track laps: lap time = pace × lap length. Lane 1 of a standard track is 400 m; outer lanes are longer.",
        "tags": [
          "calculators"
        ],
        "x-mcp-tool": "running_pace_calculator",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunningPaceCalculatorInput"
              },
              "examples": {
                "example1": {
                  "summary": "10K in 50:00: pace and splits",
                  "value": {
                    "solveFor": "pace",
                    "preset": "10k",
                    "time": "50:00",
                    "splitUnit": "km",
                    "splitStrategy": "even"
                  }
                },
                "example2": {
                  "summary": "Marathon at 5:40/km with a 3% negative split",
                  "value": {
                    "solveFor": "time",
                    "preset": "marathon",
                    "pace": "5:40",
                    "paceUnit": "km",
                    "splitUnit": "km",
                    "splitStrategy": "negative",
                    "splitPercent": 3
                  }
                },
                "example3": {
                  "summary": "How far in 1 hour at 8:30/mile?",
                  "value": {
                    "solveFor": "distance",
                    "time": "1:00:00",
                    "pace": "8:30",
                    "paceUnit": "mi",
                    "splitUnit": "mi"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RunningPaceCalculatorResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "RunningPaceCalculatorInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "solveFor": {
            "title": "Calculate",
            "type": "string",
            "enum": [
              "pace",
              "time",
              "distance"
            ],
            "default": "pace"
          },
          "preset": {
            "title": "Race distance",
            "type": "string",
            "enum": [
              "custom",
              "1k",
              "mile",
              "5k",
              "10k",
              "15k",
              "10mi",
              "half",
              "marathon",
              "50k"
            ],
            "default": "10k"
          },
          "distance": {
            "title": "Custom distance",
            "description": "Used when the race distance is \"Custom distance\".",
            "type": "number",
            "minimum": 0.001,
            "maximum": 1000
          },
          "distanceUnit": {
            "title": "Distance unit",
            "type": "string",
            "enum": [
              "km",
              "mi",
              "m"
            ],
            "default": "km"
          },
          "time": {
            "title": "Time",
            "description": "h:mm:ss or mm:ss, e.g. 1:45:00 or 24:30.",
            "type": "string",
            "maxLength": 20
          },
          "pace": {
            "title": "Pace",
            "description": "mm:ss per km or per mile, e.g. 5:30.",
            "type": "string",
            "maxLength": 20
          },
          "paceUnit": {
            "title": "Pace per",
            "type": "string",
            "enum": [
              "km",
              "mi"
            ],
            "default": "km"
          },
          "splitUnit": {
            "title": "Splits every",
            "type": "string",
            "enum": [
              "km",
              "mi"
            ],
            "default": "km"
          },
          "splitStrategy": {
            "title": "Split strategy",
            "type": "string",
            "enum": [
              "even",
              "negative",
              "positive"
            ],
            "default": "even"
          },
          "splitPercent": {
            "title": "Pace change start → finish",
            "description": "For negative/positive splits: how much the pace changes from the start to the finish, spread evenly.",
            "type": "number",
            "minimum": 0,
            "maximum": 20,
            "default": 3,
            "x-unit": "%"
          },
          "lapMeters": {
            "title": "Track lap length",
            "type": "number",
            "minimum": 50,
            "maximum": 1000,
            "default": 400,
            "x-unit": "m"
          }
        },
        "required": []
      },
      "RunningPaceCalculatorResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "distanceMeters",
              "distanceKm",
              "distanceMiles",
              "timeSeconds",
              "time",
              "paceSecPerKm",
              "paceSecPerMile",
              "pacePerKm",
              "pacePerMile",
              "speedKmh",
              "speedMph",
              "splits",
              "halves",
              "predictions",
              "laps"
            ],
            "properties": {
              "distanceMeters": {
                "type": "number"
              },
              "distanceKm": {
                "type": "number"
              },
              "distanceMiles": {
                "type": "number"
              },
              "timeSeconds": {
                "type": "number"
              },
              "time": {
                "type": "string"
              },
              "paceSecPerKm": {
                "type": "number"
              },
              "paceSecPerMile": {
                "type": "number"
              },
              "pacePerKm": {
                "type": "string"
              },
              "pacePerMile": {
                "type": "string"
              },
              "speedKmh": {
                "type": "number"
              },
              "speedMph": {
                "type": "number"
              },
              "splits": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "split",
                    "atDistance",
                    "splitTime",
                    "splitSeconds",
                    "cumulative",
                    "cumulativeSeconds",
                    "pace"
                  ],
                  "properties": {
                    "split": {
                      "type": "integer"
                    },
                    "atDistance": {
                      "type": "number"
                    },
                    "splitTime": {
                      "type": "string"
                    },
                    "splitSeconds": {
                      "type": "number"
                    },
                    "cumulative": {
                      "type": "string"
                    },
                    "cumulativeSeconds": {
                      "type": "number"
                    },
                    "pace": {
                      "type": "string"
                    }
                  }
                }
              },
              "halves": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "first",
                  "second"
                ],
                "properties": {
                  "first": {
                    "type": "string"
                  },
                  "second": {
                    "type": "string"
                  }
                }
              },
              "predictions": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "race",
                    "meters",
                    "seconds",
                    "time",
                    "pacePerKm",
                    "pacePerMile",
                    "inRange"
                  ],
                  "properties": {
                    "race": {
                      "type": "string"
                    },
                    "meters": {
                      "type": "number"
                    },
                    "seconds": {
                      "type": "number"
                    },
                    "time": {
                      "type": "string"
                    },
                    "pacePerKm": {
                      "type": "string"
                    },
                    "pacePerMile": {
                      "type": "string"
                    },
                    "inRange": {
                      "type": "boolean",
                      "description": "Both times within Riegel's 3.5–230 minute endurance range."
                    }
                  }
                }
              },
              "laps": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "lapMeters",
                  "lapTime",
                  "lapSeconds",
                  "halfLapTime",
                  "lapsForDistance"
                ],
                "properties": {
                  "lapMeters": {
                    "type": "number"
                  },
                  "lapTime": {
                    "type": "string"
                  },
                  "lapSeconds": {
                    "type": "number"
                  },
                  "halfLapTime": {
                    "type": "string"
                  },
                  "lapsForDistance": {
                    "type": "number"
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}