I found a solution which is bit hackish, but not too much. I find the first space in the line and set it to a larger height. The line in my case always have a space somewhere (and it's generally true usually), the bigger height space forces the line height to be bigger which is effectively a top margin:
(let ((pos (1- (save-excursion (beginning-of-line) (search-forward " ")))))
(put-text-property pos (1+ pos) 'display '(height 1.5)))
In the comments @rpluim suggested an even simpler alternative: putting the text property on the newline, but note that this method does not work well if the line is long and spans multiple visual lines.
(put-text-property (line-end-position) (1+ (line-end-position)) 'display '(height 1.5))