{"id":16008545,"url":"https://github.com/jimbobbennett/worlds-worst-api","last_synced_at":"2026-06-23T03:31:43.751Z","repository":{"id":224640783,"uuid":"761508560","full_name":"jimbobbennett/worlds-worst-api","owner":"jimbobbennett","description":"An API containing some of the worst design decisions known to humanity...","archived":false,"fork":false,"pushed_at":"2024-02-27T01:19:50.000Z","size":280,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-13T15:50:08.594Z","etag":null,"topics":["api","fastapi","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jimbobbennett.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-22T00:43:05.000Z","updated_at":"2024-03-01T12:22:58.000Z","dependencies_parsed_at":null,"dependency_job_id":"1d5e9c3b-cb4e-4d2b-a81e-bd3fb5cf9134","html_url":"https://github.com/jimbobbennett/worlds-worst-api","commit_stats":null,"previous_names":["jimbobbennett/worlds-worst-api"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jimbobbennett/worlds-worst-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimbobbennett%2Fworlds-worst-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimbobbennett%2Fworlds-worst-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimbobbennett%2Fworlds-worst-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimbobbennett%2Fworlds-worst-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jimbobbennett","download_url":"https://codeload.github.com/jimbobbennett/worlds-worst-api/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimbobbennett%2Fworlds-worst-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34674702,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-23T02:00:07.161Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","fastapi","python"],"created_at":"2024-10-08T12:43:18.732Z","updated_at":"2026-06-23T03:31:43.731Z","avatar_url":"https://github.com/jimbobbennett.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Worlds Worst API\n\nAn API containing some of the worst design decisions known to humanity...\n\n![A cute plushie llama looking like a devil](./img/devil-llama.webp)\n\n## Run the API\n\nThis API is written in FastAPI, and can be run using the following command:\n\n```bash\ncd src\nuvicorn main:app --reload\n```\n\nYou will need to install the dependencies first, by either installing them from the `requirements.txt` file:\n\n```bash\npip install -r requirements.txt\n```\n\nOr by running this inside the provided dev container. In the dev container, the dependencies are already installed.\n\nThis API creates a SQLite database called `tickets.db` to store user and ticket data.\n\n## All the API worst practices...\n\nThis API has all the following worst practices:\n\n- [Documentation as screenshots in Excel](#documentation-as-screenshots-in-excel)\n- [GET for all operations](#get-for-all-operations)\n- [Terrible naming](#terrible-naming)\n- [Not using path parameters](#not-using-path-parameters)\n- [Always returning 200](#always-returning-200)\n- [Non-atomic updates](#non-atomic-updates)\n- [Inconsistent data types](#inconsistent-data-types)\n\n### Documentation as screenshots in Excel\n\nAs we all know - the best documentation is not an [OpenAPI](https://www.openapis.org) spec, or fancy generated docs using the spec, but screenshots inside Excel. After all, attaching screenshots to Excel spreadsheets is even a practice internal teams at Microsoft use today (ask me how I know...).\n\nThe documentation is included in this repo in the [`Worlds Worst API documentation.xlsx`](./Worlds%20Worst%20API%20documentation.xlsx) Excel spreadsheet.\n\n\u003e Don't tell our users, but you can also access the OpenAPI spec once this app is running at [`localhost:8000/openapi.json`](http://localhost:8000/openapi.json), and generated docs at [`localhost:8000/docs`](http://localhost:8000/docs)\n\n### GET for all operations\n\n`POST`, `PUT`, `PATCH`, `DELETE`? Who needs them? Just use `GET` for everything! This API does exactly that. If you want to create a new user, you `GET` a new user using the `new_user` endpoint. If you want to add a ticket, you `GET` a new ticket using the `add_ticket` endpoint. And so on.\n\nOne endpoint for each distinct action you might want to perform, and you always make a GET request, so there are no confusions.\n\n### Terrible naming\n\nThe 4 hardest things in software development are naming things, cache invalidation, and off-by-one errors. So why stress over naming them, let's just use the first name that comes to mind when writing each endpoint.\n\n- `GET` a new user - use the `/new_user` endpoint.\n- `GET` a new ticket - use the `/add_ticket` endpoint. No need to be consistent and call it `/new_ticket` or `/ticket` or anything like that.\n- `GET` an existing ticket - use the `/tikcet` endpoint. Yeah, we spelled it wrong, but we can't change it now as other teams may be using this API already and don't have time to update their code.\n\n### Not using path parameters\n\nWe also don't want to have to worry about path parameters, as these may mean parsing the URL for the endpoint, and that sounds like hard work. You want to get a ticket? Pass the ticket ID in the bpdy of the request when calling the `/tikcet` endpoint.\n\nSame with query parameters - we don't want to have to parse these, so we just use the body for everything. You want to seach for a ticket, call the `/search_tickets` endpoint and pass what you want to search for in the body.\n\n`/ticket/{id}`? No thanks!\n\nObviously we do use path parameters for users, as one team asked for this and their boss is higher up the chain than ours. So we have `/user/{user_id}` for getting a user.\n\n### Always returning 200\n\nIf we were to return anything except 200 from our API, then the client would throw exceptions, and this might break our users apps. This is a terrible thing, and the teams that create apps using our API might complain and blame us.\n\nSo, we always return 200. Even if the request was invalid, or the server errored, or the user doesn't have permission to perform the action. We always return 200. We can then return the actual error and status code in the body of the response. That way there are no exceptions, but the client gets the error!\n\n### Non-atomic updates\n\nThere are engineers who think that API calls should be atomic, and if they fail then the system state should not change. We don't have time for such nonesense. We just update the database as we go, and if the request fails, then the system state is left in an inconsistent state.\n\nFor example, we have 2 tables for user data - `user` and `user_details`. When a user is created, we first create the user in the `user` table, and then create the user details in the `user_details` table. If the second request fails, then the user is left in the `user` table, but not in the `user_details` table. Yeah, so you can't recreate the user as the user email is unique, but that's the clients problem for passing us the wrong data. We can blame it on their team.\n\n### Inconsistent data types\n\nEach team that wants to use this API has different requirements, so we just return data in the format they expect. \n\nOne team needs ticket IDs to feed into a CSV file, so we return these from the `/tickets` endpoint as a comma separated list of numbers. Another team wants JSON (whoever he is) for the ticket body, so we return that from the `/tikcet` endpoint. There's even a reporting team that wants XML for a list of users, so the `/user/{user_id}` endpoint returns XML.\n\nWe were also asked to handle the users salary field in different currencies, so we store that as a string. That way whatever currency they want can be added to the string.\n\n## More terrible API practices\n\nThis repo shows a few bad practices that have actually been seen in the wild. Yea, really! But there ar emany more.\n\nIf you think of others, feel free to add a note to this repo, and even better, add them to the API itself. We can always make it worse!\n\nYou can find a fun reddit thread with more terrible examples in this [What are some of the worst API's you've ever had the displeasure of working with? What made them so bad? thread](https://www.reddit.com/r/ExperiencedDevs/comments/1ak1hof/what_are_some_of_the_worst_apis_youve_ever_had/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjimbobbennett%2Fworlds-worst-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjimbobbennett%2Fworlds-worst-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjimbobbennett%2Fworlds-worst-api/lists"}