static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge,
const secp256k1_pubkey* pubkey)
Since you're only dealing with pointers to structs, you just need typedefs and incomplete struct definitions that you can take from the .h files where they are defined to give the compiler enough context for the declaration. This is easy for the ones that are typedefs of structs, e.g. put this line above your function declaration:
typedef struct secp256k1_context_struct secp256k1_context;
It's safe to make an identical redeclaration, so this will work fine with or without the full headers.
The other two types are more problematic because they are typedefs of anonymous structs. The best way to do this I think is to give it a fake declaration if we don't have the real one, e.g.
#ifndef SECP256K1_H
typedef void secp256k1_pubkey;
#endif
#ifndef SECP256K1_GROUP_H
typedef void secp256k1_ge;
#endif
(You could use the real secp256k1_pubkey definition, but the _ge one depends on fields which can vary and I guess having enough context to choose the correct fields is what you were trying to avoid in the first place.)
This is slightly fragile though against changes to the library, and requires you include the secp256k1 headers before this file if at all. (Else you'll get compiler errors: make a note here in a comment about what you've done so if anyone finds this in the future they'll know how to fix it.)
You then have enough context for the function definition in your own header. Note that any calling code that isn't just passing these structures through from somewhere else will probably want the full struct definitions anyway though: why not just put this function declaration in a new .h that requires the full headers included anyway, that only a few specific places need?