Sprite->remove() takes the sprite out of the display-list. Probably playdate->sprite->free(LCDSprite *sprite); is what you need. I say "probably" because the doco is very light.
Maybe there's some further removal steps needed before calling free()?
I re-use bullet sprites, taking them off-screen and de-collide them. That seems to work OK.
Generally with embedded programming you either allocate once, or just use static variables without allocation. So reallocation didn't seem appropriate.
I'm not familiar with the free() function. It doesn't seem to appear in the SDK documentation.
I think that remove() might do the trick, because by removing the sprite from the display list, and not having previously recorded the sprite to a lasting variable, it should in theory make it garbage for the garbage collector.
In Lua, calling self:remove() is enough to destroy the sprite, as long as there are no other references to the sprite object. In your case, it sounds like the Bullet is a local variable that does not have a reference outside of the scope of the function, so it will be marked as garbage as soon as it is removed from the display list.
Depending on the amount of bullets you are creating, it might be better to have a cache of bullets and re-use them instead of destroying and creating new ones. This is because garbage collection does take a toll on performance sometimes, especially if there are a ton of objects being destroyed.
Also keep in mind that the simulator has a Malloc Log that will let you see if there is memory leaking. You can view the "Map" section and check "Automatically Refresh" to see the memory allocation in real-time.