Mercedes driver Hamilton started in second but dropped to sixth early on and only managed to recover one place in the half-hour 18-lap race.
Bottas took control of the sprint – just the second-ever after the inaugural event at Silverstone in July – and finished in front of Max Verstappen to take three points.
However, the Finn will start from the back of the grid on Sunday after taking a penalty for a power unit change, meaning that Verstappen moves into pole position.
With his second-place finish, Verstappen also extends his World Championship lead over Hamilton by two points, while Daniel Ricciardo completed the top three.
"I feel good. It feels like it has been a while to finish first in a race," Bottas told Sky Sports.
"Unfortunately I'm starting from the back tomorrow but the speed is there, so I'll be fighting and coming as high as I can. Today, I enjoy and it was a clean race. We had a good pace. All good.
"Tomorrow is not going to be easy, that's for sure. The train of cars with DRS, it's not easy but obviously strategy-wise, still a free choice of tyres for the start, so let's see if we can do something."
The race got off to a frantic start as Pierre Gasly, who triumphed on this circuit last year, crashed out on Turn 1 after clipping Ricciardo and losing control of his AlphaTauri.
The safety car was deployed for the next three laps and McLaren pair Ricciardo and Lando Norris were able to successfully stay in front of Hamilton.
1. Valtteri Bottas (Mercedes)
2. Max Verstappen (Red Bull) +2.325s
3. Daniel Ricciardo (McLaren) +14.534s
4. Lando Norris (McLaren) +18.835s
5. Lewis Hamilton (Mercedes) +20.011s
6. Charles Leclerc (Ferrari) +23.442s
7. Carlos Sainz (Ferrari) +27.952s
8. Antonio Giovinazzi (Alfa Romeo) +31.089s
9. Sergio Perez (Red Bull) +31.680s
10. Lance Stroll (Aston Martin) +38.671s
1. Max Verstappen (Red Bull) – 226.5
2. Lewis Hamilton (Mercedes) – 221.5
3. Valtteri Bottas (Mercedes) – 126
4. Lando Norris (McLaren) – 114
5. Sergio Perez (Red Bull) – 108