Update on both fronts: Translations: This is a weird one. It appears that the failing translations is not a function of how getTranslationByKey is called, but some sort of strange state that the campaign can get into. Once it's in this state, API sheetworkers can't successfully call getTranslationByKey under any circumstances. I haven't yet been able to work out exactly what circumstances cause the campaign to get broken. If I start with a super simple test campaign it doesn't happen initially. I think it may first be triggered when I try to set the campaign sheet to the Shaped Sheet (as a custom sheet) - but interestingly after this point it makes no difference if I then erase that sheet and replace it with a simple testcase - from that point on translations don't work under the API in that campaign. I also notice that initially I get a message in the API log saying something like "Loading translations into worker" - but then after it breaks I don't see this any more. Although I don't know exactly how or why the campaign gets into this state, I can usually get it to break pretty quickly. If you want to see a barebones game where this problem manifests, check out my campaign id 39204. You just have to type something into the chat and hit enter and you'll see the error in the API log - and the sheetworker it's executing is extremely simple so hopefully it should be fairly simple to look into. Callbacks: While investigating the translations issue, I noticed that in a brand new campaign with just the shaped sheet and script, the callback executed when I expected it to. The original campaign I was testing with had exactly the same code, and was executing the same commands, but had 16 characters, a few handouts and 4 maps. This suggests to me that the timeout is resulting not from extended calculation within the script itself, but from the time taken to access attributes, which, as I've noted elsewhere , appears to scale linearly with campaign size(!), perhaps due to a missing/poorly configured index, or some strange artefact of your firebase configuration. Perhaps if there's a reasonable fix for the poor performance of attribute access it might be possible to avoid having to increase the timeout - which might well end up needing to be unreasonably large on campaigns with even a moderate number of characters (e.g. 100+)?