To avoid propagating the pointer I would change *RateLimiter to RateLimiter
DecodeRequest can return Request instead of *Request, or error if not valid
Also I would replace `if userID == "" {` with `if err != nil {`. If an object is not loaded successfully returning error I think is more standard
Not that deep into Go, but I also wondered why passing by value is not the preference here. It’s already non-nil. I get it - copying large structs is bad, but are large structs the common case? I think not, also the GC will love the copy instead of pointer escape-analysis.