[{"data":1,"prerenderedAt":91},["ShallowReactive",2],{"post-\u002Fblog\u002Fscheduling-without-celery":3},{"id":4,"title":5,"body":6,"date":77,"description":78,"draft":79,"extension":80,"meta":81,"navigation":82,"path":83,"seo":84,"stem":85,"tags":86,"__hash__":90},"blog\u002Fblog\u002Fscheduling-without-celery.md","Scheduling Without Celery: APScheduler Inside FastAPI",{"type":7,"value":8,"toc":70},"minimark",[9,13,18,21,25,33,54,57,61,64],[10,11,12],"p",{},"Kickmates scores football predictions the moment real matches finish. That means three\ntime-sensitive systems running at once: a rate-limited external API, a background scoring\nengine, and a real-time push layer. The obvious tool is Celery. I didn't reach for it.",[14,15,17],"h2",{"id":16},"the-constraint","The constraint",[10,19,20],{},"API-Football allows 90 calls per minute. A naive poll-everything loop would burn the quota\nin hours or score predictions minutes late — either one kills the live feel the product is\nbuilt on.",[14,22,24],{"id":23},"the-decision","The decision",[10,26,27,28,32],{},"Celery is correct at scale, but it adds a broker, a worker container, and operational weight\nthe product didn't need yet. I embedded ",[29,30,31],"strong",{},"APScheduler"," in FastAPI's lifespan with a tiered\nschedule:",[34,35,36,40,48,51],"ul",{},[37,38,39],"li",{},"Fixture statuses sync once daily at 11 PM UTC",[37,41,42,43,47],{},"Scoring runs every 50 minutes, targeting only ",[44,45,46],"code",{},"is_scored = False"," matches",[37,49,50],{},"Reminders fire hourly",[37,52,53],{},"Gameweek advancement runs nightly at 1 AM",[10,55,56],{},"A sliding-window rate limiter with async locking keeps concurrent jobs from racing to the\nsame quota.",[14,58,60],{"id":59},"the-trade-off","The trade-off",[10,62,63],{},"APScheduler is single-instance. If the server restarts mid-job, the scheduler doesn't survive\nwithout coordination logic. Acceptable here: missed windows recover on the next 50-minute\ncycle with no data loss, and the current scale doesn't justify Celery's overhead.",[10,65,66,67],{},"The lesson isn't \"never use Celery.\" It's: ",[29,68,69],{},"pick the tool that fits the constraint in front\nof you, not the one that fits a scale you don't have yet.",{"title":71,"searchDepth":72,"depth":72,"links":73},"",2,[74,75,76],{"id":16,"depth":72,"text":17},{"id":23,"depth":72,"text":24},{"id":59,"depth":72,"text":60},"2026-04-18","How Kickmates scores live football predictions on a tiered schedule without exhausting an external API — and why I skipped Celery.",false,"md",{},true,"\u002Fblog\u002Fscheduling-without-celery",{"title":5,"description":78},"blog\u002Fscheduling-without-celery",[87,88,89],"Python","FastAPI","Architecture","hdT9FJcXl2AFPXDIBvH6cKJcgfl5TpJU8wMxgxksbxY",1781109090916]